MySQL實戰(zhàn)45講Day11----一條SQL語句執(zhí)行時為什么會忽快忽慢

一、 基本概念:

1、當(dāng)內(nèi)存數(shù)據(jù)頁跟磁盤數(shù)據(jù)頁內(nèi)容不一致的時候,稱這個內(nèi)存頁為“臟頁”。內(nèi)存數(shù)據(jù)寫入到磁盤后(內(nèi)存里的數(shù)據(jù)寫入磁盤的過程叫做flush。),內(nèi)存和磁盤上的數(shù)據(jù)頁的內(nèi)容就一致了,稱為“干凈頁”。不論是臟頁還是干凈頁,都在內(nèi)存中。
2、平時執(zhí)行很快的更新操作,其實就是在寫內(nèi)存和日志,而MySQL偶爾“抖”一下的那個瞬間,可能就是在刷臟頁(flush)。

3、引發(fā)數(shù)據(jù)庫的flush過程的四種情況:

?1)、第一種情況是InnoDB的redo log寫滿了。這時候系統(tǒng)會停止所有更新操作,把checkpoint往前推進(jìn),redo log留出空間可以繼續(xù)寫。checkpoint可不是隨便往前修改一下位置就可以的。在把checkpoint位置從CP推進(jìn)到CP'時,就需要將兩個點之間的日志對應(yīng)的所有臟頁都flush到磁盤上。之后,之前可以寫入redo log的區(qū)域就從圖中從write pos到CP之間變?yōu)榱藦膚rite pos 到CP'。
? 2)、第二種情況是系統(tǒng)內(nèi)存不足。當(dāng)需要新的內(nèi)存頁,而內(nèi)存不夠用的時候,就要淘汰一些數(shù)據(jù)頁,空出內(nèi)存給別的數(shù)據(jù)頁使用。如果淘汰的是“臟頁”,就要先將臟頁寫到磁盤。如果刷臟頁一定會寫盤,就保證了每個數(shù)據(jù)頁有兩種狀態(tài):
? ?a、一種是內(nèi)存里存在,內(nèi)存里就肯定是正確的結(jié)果,直接返回;
? ?b、另一種是內(nèi)存里沒有數(shù)據(jù),就可以肯定數(shù)據(jù)文件上是正確的結(jié)果,讀入內(nèi)存后返回。這樣的效率最高。
? 3)第三種情況是MySQL認(rèn)為系統(tǒng)“空閑”的時候。當(dāng)然,即使是系統(tǒng)“忙碌”的時候,也要見縫插針地找時間,只要有機(jī)會就刷一點“臟頁”。
? 4)、第四種情況是MySQL正常關(guān)閉的時候。這時候,MySQL會把內(nèi)存的臟頁都flush到磁盤上,這樣下次MySQL啟動的時候,就可以直接從磁盤上讀數(shù)據(jù),啟動速度會很快。

4、引發(fā)數(shù)據(jù)庫的flush過程的四種情況對性能的影響:

? 1)、“redo log寫滿了,要flush臟頁”,這種情況是InnoDB要盡量避免的。因為出現(xiàn)這種情況的時候,整個系統(tǒng)就不能再接受更新了,所有的更新都必須堵住。如果你從監(jiān)控上看,這時候更新數(shù)會跌為0
? 2)、“內(nèi)存不夠用了,要先將臟頁寫到磁盤”,這種情況其實是常態(tài)。InnoDB用緩沖池(buffer pool)管理內(nèi)存,緩沖池中的內(nèi)存頁有三種狀態(tài):第一種是,還沒有使用的;第二種是,使用了并且是干凈頁;第三種是,使用了并且是臟頁。InnoDB的策略是盡量使用內(nèi)存,因此對于一個長時間運行的庫來說,未被使用的頁面很少。而當(dāng)要讀入的數(shù)據(jù)頁沒有在內(nèi)存的時候,就必須到緩沖池中申請一個數(shù)據(jù)頁。這時候只能把最久不使用的數(shù)據(jù)頁從內(nèi)存中淘汰掉:如果要淘汰的是一個干凈頁,就直接釋放出來復(fù)用;但如果是臟頁呢,就必須將臟頁先刷到磁盤,變成干凈頁后才能復(fù)用。
? 3)、因為系統(tǒng)處于“空閑”的狀態(tài)中,所以不需要關(guān)心其對性能的影響。
? 4)、因為數(shù)據(jù)庫本來就要關(guān)閉了,所以不需要關(guān)心其對性能的影響。

5、刷臟頁會明顯影響性能的情況:

? 1)、一個查詢要淘汰的臟頁個數(shù)太多,會導(dǎo)致查詢的響應(yīng)時間明顯變長;
? 2)、日志寫滿,更新全部堵住,寫性能跌為0,這種情況對敏感業(yè)務(wù)來說,是不能接受的。

二、InnoDB刷臟頁的控制策略:

首先,測試出InnoDB所在主機(jī)的IO能力,這樣InnoDB才能知道需要全力刷臟頁的時候,可以刷多快。也就是innodb_io_capacity這個參數(shù),它的作用是告訴InnoDB你的磁盤能力。這個值建議設(shè)置成磁盤的IOPS。磁盤的IOPS可以通過fio這個工具來測試,下面的語句是用來測試磁盤隨機(jī)讀寫的命令:

 fio -filename=$filename -direct=1 -iodepth 1 -thread -rw=randrw -ioengine=psync -bs=16k -size=500M -numjobs=10 -runtime=10 -group_reporting -name=mytest 

又因為磁盤能力不能只用來刷臟頁,還需要服務(wù)用戶請求。所以InnoDB會控制引擎按照“全力”的百分比來刷臟頁。

1、InnoDB的刷盤速度就是要參考的兩個因素:

?①臟頁比例,參數(shù)innodb_max_dirty_pages_pct是臟頁比例上限,默認(rèn)值是75%。要合理地設(shè)置innodb_io_capacity的值,并且平時多關(guān)注臟頁比例,不要讓它經(jīng)常接近75%。其中,臟頁比例是通過Innodb_buffer_pool_pages_dirty/Innodb_buffer_pool_pages_total得到。
?②redo log寫盤速度。

2、InnoDB的刷盤速度的計算方法:

InnoDB會根據(jù)當(dāng)前的臟頁比例(假設(shè)為M),算出一個范圍在0到100之間的數(shù)字,記為F1(M)。InnoDB每次寫入的日志都有一個序號,當(dāng)前寫入的序號跟checkpoint對應(yīng)的序號之間的差值(假設(shè)為N)。InnoDB會根據(jù)這個N算出一個范圍在0到100之間的數(shù)字,這個計算公式可以記為F2(N)。N越大,算出來的值越大。根據(jù)上述算得的F1(M)和F2(N)兩個值,取其中較大的值記為R,之后引擎就可以按照innodb_io_capacity定義的能力乘以R%來控制刷臟頁的速度。

3、flush臟頁時的鄰居連坐機(jī)制:

??在準(zhǔn)備刷一個臟頁的時候,如果這個數(shù)據(jù)頁旁邊的數(shù)據(jù)頁剛好是臟頁,就會把這個“鄰居”也帶著一起刷掉;而且這個把“鄰居”拖下水的邏輯還可以繼續(xù)蔓延,也就是對于每個鄰居數(shù)據(jù)頁,如果跟它相鄰的數(shù)據(jù)頁也還是臟頁的話,也會被放到一起刷。在InnoDB中,innodb_flush_neighbors 參數(shù)就是用來控制這個行為的,值為1的時候會有上述的“連坐”機(jī)制,值為0時表示不找鄰居,自己刷自己的。找“鄰居”這個優(yōu)化在機(jī)械硬盤時代是很有意義的,可以減少很多隨機(jī)IO。機(jī)械硬盤的隨機(jī)IOPS一般只有幾百,相同的邏輯操作減少隨機(jī)IO就意味著系統(tǒng)性能的大幅度提升。而如果使用的是SSD這類IOPS比較高的設(shè)備的話,建議把innodb_flush_neighbors的值設(shè)置成0。因為這時候IOPS往往不是瓶頸,而“只刷自己”,就能更快地執(zhí)行完必要的刷臟頁操作,減少SQL語句響應(yīng)時間。

三、如果一個高配的機(jī)器,redo log設(shè)置太小,會發(fā)生什么情況?

??因為每次事務(wù)提交都要寫redo log,如果設(shè)置太小,很快就會被寫滿,write pos一直追著CP。這時候系統(tǒng)不得不停止所有更新,去推進(jìn)checkpoint。這時,你看到的現(xiàn)象就是磁盤壓力很小,但是數(shù)據(jù)庫出現(xiàn)間歇性的性能下跌。

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時請結(jié)合常識與多方信息審慎甄別。
平臺聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點,簡書系信息發(fā)布平臺,僅提供信息存儲服務(wù)。

相關(guān)閱讀更多精彩內(nèi)容

友情鏈接更多精彩內(nèi)容