認(rèn)識(shí)Mysql中的緩存InnoDB Buffer Poll

?如何提高系統(tǒng)的讀的效率?使用緩存。如何提高系統(tǒng)寫的效率?使用緩存。提高讀寫效率的關(guān)鍵在于減小磁盤I/O,特別是隨機(jī)磁盤I/O。平時(shí)我們使用緩存主要用在提高查詢效率上面,很少在提高寫入效率上考慮緩存。本文將介紹Mysql在讀寫中如何運(yùn)用緩存才提高自身的讀寫效率。

Buffer Poll

定義

?buffer poll是InnoDB維護(hù)的一個(gè)內(nèi)存區(qū)域,用于存放表和索引數(shù)據(jù)。buffer poll的存在加快了mysql的讀寫速度。buffer poll是按頁存儲(chǔ)數(shù)據(jù),并用鏈表的數(shù)據(jù)結(jié)構(gòu)對(duì)buffer poll進(jìn)行管理。

淘汰算法

?buffer poll既然是內(nèi)存中的區(qū)域,數(shù)據(jù)大小是有限的。就需要對(duì)內(nèi)存區(qū)域的進(jìn)行管理,使用的是LRU(Least recently used)算法,只不過Mysql對(duì)于傳統(tǒng)的LRU算法做了一些改進(jìn),提高自己的緩存命中率。
- LUR
經(jīng)典的LUR算法,使用一個(gè)鏈表鏈表實(shí)現(xiàn),在進(jìn)行數(shù)據(jù)讀寫操作的時(shí)候如下步驟:

  1. 數(shù)據(jù)存在鏈表中,將其移動(dòng)到頭節(jié)點(diǎn);
  2. 數(shù)據(jù)不存在鏈表中,將數(shù)據(jù)放入頭結(jié)點(diǎn),并且這是判斷是否超過了鏈表的長(zhǎng)度,如果超過了限制長(zhǎng)度,將尾節(jié)點(diǎn)淘汰。


    LUR

?這種LUR算法看似沒有什么問題。但是在Mysql中如果出現(xiàn)查詢了一個(gè)很冷門數(shù)據(jù)量的又很大的表這種業(yè)務(wù)場(chǎng)景時(shí),就會(huì)出現(xiàn)問題。假設(shè)我現(xiàn)在buffer poll只有50M,但是我查詢了一個(gè)數(shù)據(jù)量有100M的表,這個(gè)表平時(shí)又很少使用,如果使用LUR算法就會(huì)把以前緩存的熱點(diǎn)數(shù)據(jù)全部淘汰掉,而新放入buffer poll中的數(shù)據(jù)很長(zhǎng)一段時(shí)間都不會(huì)再訪問,這樣新的操作又要進(jìn)行大量的磁盤I/O才能將熱點(diǎn)數(shù)據(jù)重新緩存回來。所以mysql針對(duì)這種情況對(duì)LUR算法做了一些改變。
- 變種LUR

圖片來自官網(wǎng)

?mysql將buffer poll 列表按照5:3的比例分為新舊兩個(gè)部分,這樣設(shè)計(jì)之后,該算法的讀寫操作步驟如下:

  • 數(shù)據(jù)在new sublist區(qū)域,將數(shù)據(jù)移動(dòng)到y(tǒng)ong區(qū)域的頭結(jié)點(diǎn);
  • 數(shù)據(jù)不存在buffer pool中,將其插入中點(diǎn)(old sublist的頭結(jié)點(diǎn)),淘汰掉old sublist的尾節(jié)點(diǎn);
  • 數(shù)據(jù)在old sublist中,若這個(gè)數(shù)據(jù)頁在 LRU 鏈表中存在的時(shí)間超過了 1 秒,就把它移動(dòng)到new sublist的頭結(jié)點(diǎn),如果這個(gè)數(shù)據(jù)頁在 LRU 鏈表中存在的時(shí)間短于 1 秒,位置保持不變。1 秒這個(gè)時(shí)間,是由參數(shù) innodb_old_blocks_time 控制的。其默認(rèn)值是 1000,單位毫秒。

?采用這種算法,那種不帶where條件的select操作,不會(huì)導(dǎo)致buffer poll中的緩存的數(shù)據(jù)受到很大的影響。因?yàn)檫@種查詢都是順序讀,只會(huì)存在于old sublist,在old sublist中就會(huì)淘汰掉,而不會(huì)影響到new sublist中的數(shù)據(jù)。

更新優(yōu)化

?上面是buffer pool對(duì)查詢的優(yōu)化,這里將介紹buffer pool對(duì)數(shù)據(jù)更新的優(yōu)化。首先,先要知道m(xù)ysql在進(jìn)行一個(gè)更新操作的步驟是什么。


更新操作

?由上面的圖可知,mysql執(zhí)行一個(gè)更新操作,并不是直接去更改磁盤上的數(shù)據(jù),而是去更改緩存并標(biāo)記該頁為臟頁,然后寫入日志作為持久化。這樣的一個(gè)更新流程首先更改緩存中的數(shù)據(jù)比更改磁盤上的數(shù)據(jù)要快上很多,然后寫入日志這一步是順序?qū)?,沒有隨機(jī)I/O,操作也很快。然后mysql等待一個(gè)合適的時(shí)機(jī)將臟頁的數(shù)據(jù)刷入磁盤。
?這種緩存策略被稱為Write Back(寫回)策略,操作系統(tǒng)的page cache也是采用的這種策略,提高了寫的效率。

Change Buffer

?change buffer是針對(duì)非聚簇索引(secondary index)的數(shù)據(jù)更改(insert、update、delete)的優(yōu)化而存在的特殊的數(shù)據(jù)結(jié)構(gòu)。在對(duì)非聚簇索引進(jìn)行更改操作的時(shí)候,如果數(shù)據(jù)不在內(nèi)存中,先將更改操作先記錄到change buffer中,在之后的查詢需要訪問這個(gè)數(shù)據(jù)頁的時(shí)候,將數(shù)據(jù)頁讀入內(nèi)存,然后執(zhí)行 change buffer 中與這個(gè)頁有關(guān)的操作。
以下為官網(wǎng)介紹:

The change buffer is a special data structure that caches changes to secondary index pages when those pages are not in the buffer pool. 
The buffered changes, which may result from INSERT, UPDATE, or DELETE operations (DML), 
are merged later when the pages are loaded into the buffer pool by other read operations.
圖片來自官網(wǎng)

?為什么是非聚簇索引才能使用change buffer。因?yàn)椴幌窬鄞厮饕?,非聚簇索引通常非唯一,更改一個(gè)非聚簇索引順序相對(duì)比較隨機(jī),將更改操作先寫入chang buffer,隨后做一個(gè)合并操作,這樣可以避免查找非聚餐索引到內(nèi)存所產(chǎn)生的大量隨機(jī)I/O。如果是唯一索引不能使用change buffer,因?yàn)樾枰獙?shù)據(jù)加載到內(nèi)存中判斷索引是否已經(jīng)存在,所以唯一索引和非唯一索引在更改數(shù)據(jù)會(huì)多一步將數(shù)據(jù)加載到內(nèi)存的動(dòng)作。

總結(jié)

?本文了解了mysql緩存的作用,怎么提高讀的效率和怎么提高寫的效率。這些都是需要學(xué)習(xí)的知識(shí)點(diǎn),以后如果遇到類似的場(chǎng)景將經(jīng)典的解決方案運(yùn)用到自己的項(xiàng)目之中。

參考:
https://dev.mysql.com/doc/refman/8.0/en/innodb-buffer-pool.html
https://dev.mysql.com/doc/refman/8.0/en/innodb-change-buffer.html
普通索引和唯一索引,應(yīng)該怎么選擇?
日志系統(tǒng):一條SQL更新語句是如何執(zhí)行的?

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

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

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