1. 程式人生 > >為什麼MySQL做查詢語句時,第一次會很慢,但是第二次,第三次就會變快

為什麼MySQL做查詢語句時,第一次會很慢,但是第二次,第三次就會變快

為什麼MySQL做查詢語句時,第一次會很慢,但是第二次,第三次就會變快


為什麼MySQL的查詢事務第一次執行會很慢,第二次,第三次就會快很多呢?

在國外,有個老外這麼提問

Hi, I have an sql query which takes 8 seconds in the first run. The next run there after takes 0.5 seconds and all consecutive runs take 0.5 seconds. Is the plan getting cached? How do i make this query run in 0.5 second in the first run itself? Please find the query below.

翻譯:

我有一個SQL查詢,第一次執行需要8秒。 之後的下一次執行需要0.5秒,所有連續執行需要0.5秒。 該計劃是否被快取? 如何在第一次執行中以0.5秒的速度執行此查詢? 請在下面找到查詢。

select isnull(joblabor.IDNumber,-1) 'ProcessTransactionID',EmpNo 'EmployeeID',case(joblabor.lJob) when '-1' then '0' else joblabor.ljob end 'JobID',
joblabor.ProcNo 'ActivityID',process.Process 'ActvityName', joblabor.shift 'Shift',case (isnull(suspend,0)) when 1 then 'true' else 'false' end 'Suspended',
joblabor.StartTime 'StartDateTime',joblabor.starttime 'StartTime', joblabor.updated 'UpdatedDate',
ProcQuant 'Quantity',Prochours 'Hours',isnull(Remarks,'') 'Remark',IsNull(JCNotes,'') 'Notes',IsNULL(JobFormSpecs.PartDes,'') as FormDesc,
IsNull(Job.EstDes1,'') 'JobDesc',0 'Standard',0 'MinimumStd',0 'MaximumStd',JobLabor.PartNo 'FormID',joblabor.CostDate 'CostDate',isnull(JobLabor.linenum,1) 'ProcessTransactionIndex',case(joblabor.suspend) when 1 then 1 else 0 end 'ProcessType'
,(joblabor.timepct*100) 'Percent',IsNull(Job.FCustNo,'') 'CustomerID', IsNull(Job.FCompany,'') 'CustomerName' , joblabor.EndTime 'EndDateTime',process.ProcGroup 'ActivityGroup',
case (LEN(LTRIM(SUBSTRING(Job.remark1, 1, 25)))) when 0 then 'false' else 'true' end 'HasJobNotes' ,
case (isnull(ProcessRemarks.ContainsProcessRemarks,0)) when 0 then 'false' else 'true' end 'HasProcessRemarks' ,
case (isnull(ChangeOrder.ContainsChangeOrders,0)) when 0 then 'false' else 'true' end 'HasAlteration'
,isnull(joblabor.costcode,'') 'CostStatus'
,isnull(Complete,'') 'CompletionCode',GRYield 'GrossYield',NetYield 'NetYield'
from BBJobCST joblabor (nolock)
Left Outer Join bbjthead Job (nolock) on (joblabor.lJob = Job.lJob)
inner join SSProces as Process (nolock) on( process.ProcNo = joblabor.ProcNo AND process.archive = 0)
left outer join bbPthead JobFormSpecs (nolock) on (ltrim(rtrim(joblabor.Ljob)) = ltrim(rtrim(JobFormSpecs.LJob)) and ltrim(rtrim(JobLabor.PartNo))=ltrim(rtrim(jobFormSpecs.PartNo)) )
left outer join (
SELECT Count(bbchghdr.ljob) 'ContainsChangeOrders', bbchghdr.ljob FROM bbchghdr (nolock)
inner join bbchglin (nolock) ON bbchghdr.ljob = bbchglin.ljob AND bbchghdr.changeno = bbchglin.changeno
WHERE type = 'P' AND descript NOT LIKE '' group by bbchghdr.ljob) ChangeOrder on ChangeOrder.ljob=joblabor.ljob
left join (
SELECT Count(bbjobcst.ljob) 'ContainsProcessRemarks', bbjobcst.ljob
FROM bbjobcst (nolock) WHERE ( LEN(LTRIM(SUBSTRING(bbjobcst.jcnotes, 1, 25))) > 0 or LEN(LTRIM(remarks)) > 0)
Group By bbjobcst.ljob) ProcessRemarks on ProcessRemarks.ljob=joblabor.ljob
where joblabor.empno = '000002013' and
(isnull(joblabor.endtime,'') = '' or suspend=1 ) and (joblabor.ProcHours = 0 or suspend=1 )
and joblabor.ljob <> 0
order by joblabor.Costdate desc,joblabor.starttime desc,[linenum] asc

回答:

It isn’t the query plan that is getting cached – it is the actual data and indexes which are being cached. This is common behavior of any database. If you ran the query, then waited a while and ran again (keeping the session active), you would see the query take longer again, based on the cached data/indexes being flushed to make room for other data.
You can see if you can reduce the 0.5 seconds repeat time, and/or you can see if you can reduce the intermediate result sets (which are usually what get cached and speed up the queries the second…nth runs).
If the query is producing a large intermediate result set (perhaps a large join where most records are then discarded), you may be able to speed it up by changing parts of your query. Also, sometimes just adding the right index can solve issues like this.
Look at the execution plan and see if there are any “table access full”, “index access full”, “cartesian”, or similar joins indicating inefficient join/indexing.

翻譯:

意思就是說Sql語句第一次查詢慢的原因不僅僅是因為執行計劃沒有被快取這麼簡單,有時候你會發現Sql語句重用了執行計劃,但是第一次查詢還是很慢。就如同上面回答一樣,最主要的原因是第一次查詢的時候,mysql會將查詢出的部分資料和索引從磁碟載入到記憶體作為快取,而第二次查詢的時候就直接從記憶體快取中拿出資料了,自然要比從磁碟上載入資料快很多,mysql 會定期清除快取,所以一段Sql語句如果長期不執行後,就需要從磁碟從新載入資料。