MySQL的刷脏机制
文章目錄
- 緩沖池 Buffer Pool
- 刷臟頁的時(shí)機(jī)
- MySQL定時(shí)刷
- MySQL內(nèi)存(buffer pool)不足的時(shí)候
- MySQL正常關(guān)閉的時(shí)候
- redo log滿了的時(shí)候
- 刷臟導(dǎo)致的性能問題
- 控制刷臟頁速度的因素
先了解下前置知識(shí):
緩沖池 Buffer Pool
首先,對(duì)于InnoDB存儲(chǔ)引擎來說,數(shù)據(jù)都是放在磁盤上的,存儲(chǔ)引擎要操作數(shù)據(jù),必須先把磁盤里面的數(shù)據(jù)加載到內(nèi)存里面才可以操作。
??這里就有個(gè)問題,是不是我們需要的數(shù)據(jù)多大,我們就一次從磁盤加載多少數(shù)據(jù)到內(nèi)存呢?比如我要讀6個(gè)字節(jié)。
??磁盤I/O的讀寫相對(duì)于內(nèi)存的操作來說是很慢的。如果我們需要的數(shù)據(jù)分散在磁盤的不同的地方,那就意味著會(huì)產(chǎn)生很多次的I/O操作。
??所以,無論是操作系統(tǒng)也好,還是存儲(chǔ)引擎也好,都有一個(gè)預(yù)讀取的概念。也就是說,當(dāng)磁盤上的一塊數(shù)據(jù)被讀取的時(shí)候,很有可能它附近的位置也會(huì)馬上被讀取到,這個(gè)就叫做局部性原理。那么這樣,我們干脆每次多讀取一點(diǎn),而不是用多少讀多少。
??InnoDB設(shè)定了一個(gè)存儲(chǔ)引擎從磁盤讀取數(shù)據(jù)到內(nèi)存的最小的單位,叫做頁。操作系統(tǒng)也有頁的概念。操作系統(tǒng)的頁大小一般是4K,而在InnoDB里面,這個(gè)最小的單位默認(rèn)是16KB大小。如果要修改這個(gè)值的大小,需要清空數(shù)據(jù)重新初始化服務(wù)。
舉個(gè)例子,你去燒烤店跟老板說,老板,來一個(gè)生蠔。他根本不賣,懶得給你烤。老板賣給你生蠔,就是一打一打地賣。
??我們要操作的數(shù)據(jù)就在這樣的頁里面,數(shù)據(jù)所在的頁叫數(shù)據(jù)頁。
這里有一個(gè)問題,操作數(shù)據(jù)的時(shí)候,每次都要從磁盤讀取到內(nèi)存(再返回給Server),有沒有什么辦法可以提高效率?
??還是緩存的思想。把讀取過的數(shù)據(jù)頁緩存起來。
??InnoDB設(shè)計(jì)了一個(gè)內(nèi)存的緩沖區(qū)。讀取數(shù)據(jù)的時(shí)候,先判斷是不是在這個(gè)內(nèi)存區(qū)域里面,如果是,就直接讀取,然后操作,不用再次從磁盤加載。如果不是,讀取后就寫到這個(gè)內(nèi)存的緩沖區(qū)。
??這個(gè)內(nèi)存區(qū)域有個(gè)專屬的名字,叫 Buffer Pool。
??修改數(shù)據(jù)的時(shí)候,也是先寫入到 buffer pool,而不是直接寫到磁盤。當(dāng)數(shù)據(jù)在緩存中,也就是內(nèi)存的數(shù)據(jù)頁和磁盤數(shù)據(jù)不一致的時(shí)候,我們把它叫做臟頁。那臟頁什么時(shí)候才同步到磁盤呢?
??InnoDB里面有專門的后臺(tái)線程把Buffer Pool的數(shù)據(jù)寫入到磁盤,每隔一段時(shí)間就一次性地把多個(gè)修改寫入磁盤,這個(gè)動(dòng)作就叫做刷臟。
刷臟頁的時(shí)機(jī)
MySQL定時(shí)刷
MySQL會(huì)在自認(rèn)為系統(tǒng)“空閑”的時(shí)候或者當(dāng)系統(tǒng)更新很頻繁,redo log很快就寫滿的情況下,合理的定時(shí)進(jìn)行刷臟
MySQL內(nèi)存(buffer pool)不足的時(shí)候
當(dāng)需要將數(shù)據(jù)頁讀到內(nèi)存中時(shí),發(fā)現(xiàn)內(nèi)存不夠了,就需要淘汰掉內(nèi)存中最不經(jīng)常使用的數(shù)據(jù)頁,空出內(nèi)存給別的數(shù)據(jù)頁使用,如果淘汰的是“臟頁”,就要先把臟頁寫到磁盤中。
MySQL正常關(guān)閉的時(shí)候
如果關(guān)閉的時(shí)候不刷臟,啟動(dòng)的時(shí)候就需要去讀redo log然后同步數(shù)據(jù)到磁盤,這樣啟動(dòng)速度會(huì)變慢。
redo log滿了的時(shí)候
redo log寫滿的時(shí)候,整個(gè)系統(tǒng)就不能再接收更新了,所有的更新必須都阻塞住。這種情況要盡量避免。
刷臟導(dǎo)致的性能問題
一個(gè)查詢要淘汰的臟頁個(gè)數(shù)太多,會(huì)導(dǎo)致查詢的相應(yīng)時(shí)間明顯變長
日志寫滿,更新全部讀,寫性能跌為0,這種情況對(duì)敏感業(yè)務(wù)來說,是不能接受的
因此InnoDB要控制臟頁比例,來盡量避免這兩種情況
需要正確告訴InnoDB所在主機(jī)的IO能力,這樣InnoDB才能知道需要全力刷臟頁的時(shí)候,可以刷多快。
可以通innodb_io_capacity參數(shù)來控制InnoDB的刷盤能力,這個(gè)值建議設(shè)置成磁盤的IOPS,通過fio工具可以測(cè)試出磁盤的IOPS,命令如下:
innodb_io_capacity的默認(rèn)值為200,正確配置innodb_io_capacity可以發(fā)揮機(jī)器的性能,錯(cuò)誤配置也會(huì)導(dǎo)致性能問題,比如使用SSD的硬盤就可以將這個(gè)值配大些,但是也不能配置過大,配置過大會(huì)導(dǎo)致InnoDB把磁盤的能力全用來刷臟頁了,不能服務(wù)用戶請(qǐng)求。
控制刷臟頁速度的因素
如果刷臟頁慢,會(huì)導(dǎo)致內(nèi)存臟頁太多,其次是redo log寫滿(因?yàn)榕K頁還沒有同步到磁盤,redo log就不能覆寫)。
InnoDB的刷盤速度就通過臟頁比例和redo log寫盤速度來控制的.
為了減少刷臟給業(yè)務(wù)帶來的影響,要合理的設(shè)置innodb_io_capacity的值,并且平時(shí)要多關(guān)注臟頁比例,不要讓它經(jīng)常解決75%。
--臟頁比例計(jì)算: select VARIABLE_VALUE into @a from performance_schema.global_status where VARIABLE_NAME = 'Innodb_buffer_pool_pages_dirty'; select VARIABLE_VALUE into @b from performance_schema.global_status where VARIABLE_NAME = 'Innodb_buffer_pool_pages_total'; select @a/@b;連帶機(jī)制
一旦一個(gè)查詢請(qǐng)求需要在執(zhí)行過程中刷掉一個(gè)臟頁時(shí),這個(gè)查詢就可能要比平時(shí)慢了,MySQL中的一個(gè)機(jī)制可能會(huì)讓查詢更慢。
在準(zhǔn)備刷一個(gè)臟頁的時(shí)候,如果這個(gè)數(shù)據(jù)頁旁邊的數(shù)據(jù)頁剛好是臟頁,就會(huì)把這個(gè)“鄰居”也帶著一起刷掉,并且這個(gè)邏輯會(huì)繼續(xù)蔓延。
通過innodb_flush_neighbors可以控制這個(gè)行為,值為1的時(shí)候會(huì)有上述的連帶機(jī)制,MySQL8.0以下默認(rèn)為1。
?
建議:
當(dāng)使用的是機(jī)械硬盤時(shí),建議開啟,這樣可以減少很多的隨機(jī)IO
當(dāng)使用的是固態(tài)硬盤這類IOPS比較高的設(shè)備時(shí),建議關(guān)閉,因?yàn)檫@個(gè)時(shí)候IOPS往往不是瓶頸,這樣可以減少SQL的響應(yīng)時(shí)間
總結(jié)
以上是生活随笔為你收集整理的MySQL的刷脏机制的全部內(nèi)容,希望文章能夠幫你解決所遇到的問題。