你知道MySQL的LRU連結串列嗎?

賜我白日夢發表於2020-11-16

相信大家對LRU連結串列是不陌生的,算是一種基礎的資料結構!

LRU:Least Recently Used


一、簡述傳統的LRU連結串列

LRU:Least Recently Used

相信大家對LRU連結串列是不陌生的,它算是一種基礎的資料結構吧,而且想必面試時也被問到過什麼是LRU連結串列,甚至是讓你手寫一個LRU連結串列。

如果你讀了上一篇:你有沒有搞混查詢快取和BufferPool?談談看!

想必你已經知道了MySQL的Buffer Pool機制以及MySQL組織資料的最小單位是資料頁。並且你也知道了 資料頁在Buffer Pool中是以LRU連結串列的資料結構組織在一起的。

其實所謂的LRU連結串列本質上就是一個雙向迴圈連結串列,如下圖:

下面我們結合LRU連結串列和資料頁機制描述一下MySQL載入資料的機制:

我們將從磁碟中讀取的資料頁稱為young page,young page會被直接放在連結串列的頭部。已經存在於LRU連結串列中資料頁如果被使用到了,那麼該資料頁也被認為是young page而被移動到連結串列頭部。這樣連結串列尾部的資料就是最近最少使用的資料了,當Buffer Pool容量不足,或者後臺執行緒主動重新整理資料頁時,就會優先重新整理連結串列尾部的資料頁。


二、傳統LRU連結串列的不足

相信你之前肯定聽說過作業系統級別的空間區域性性原理:

spatial locality(空間區域性性):也就是說讀取一個資料,在它周圍記憶體地址儲存的資料也很有可能被讀取到,於是作業系統會幫你預讀一部分資料。

MySQL也是存在存在預讀機制的!

  1. 當Buffer Pool中儲存著一個區中13個連續的資料頁時,你再去這個區裡面讀取,MySQL就會將這個區裡面所有的資料頁都載入進Buffer Pool中的LRU連結串列中。(然後可能你根本不會使用這些被預讀的資料頁)
  2. 當你順序的訪問了一個區中大於 innndb_read_ahead_threshold=56個資料頁時,MySQL會自動幫你將下一個相鄰區中的資料頁讀入LRU連結串列中。(這個機制預設是被關閉的)
  3. 當你執行select * from xxx;時,如果表中的資料頁非常多,那這些資料頁就會一一將Buffer Pool中的經常使用的快取頁擠下去,可能留在LRU連結串列中的全部是你不經常使用的資料。

綜上你可以看到,所謂的預讀機制的優勢,實際上違背了LRU去實現將最近最少使用的資料頁刷入磁碟的設計初衷。


三、MySQL的LRU連結串列

接下來我們看下MySQL的Buffer Pool是如何定製LRU連結串列的,已經LRU幫InnoDB解決了什麼問題。

當業務進行大量的CRUD時,需要不斷的將資料頁讀取到buffer pool中的LRU連結串列中。

MySQL的LRU連結串列長下面這樣。

LRU連結串列被MidPoint分成了New Sublist和Old Sublist兩部分。

其中New Sublist大概佔比5/8,Old Sublist佔比3/8。

New Sublist儲存著young page,而Old Sublist儲存著Old Page。

我們可以通過如下的方式檢視MidPoint的預設值。

使用者可以根據自己的業務動態的調整這個引數!

這其實是一種冷熱資料分離設計思想。他相對於傳統的LRU連結串列有很大的優勢


四、MySQL定製LRU連結串列的優勢

而對於MySQLLRU連結串列來說,通過MidPoint將連結串列分成兩部分。

從磁碟中新讀出的資料會放在Old Sublist的頭部。這樣即使你真的使用select * from t;也不會導致New Sublist中的經常被訪問的資料頁被刷入磁碟中。

正常情況下,訪問Old Sublist中的快取頁,那麼該快取頁會被提升到New Sublist中成為熱資料。

但是當你通過 select * from t 將一大批資料載入到Old Sublist時,然後在不到1s內你又訪問了它,那在這段時間內被訪問的快取頁並不會被提升為熱資料。 這個1s由引數innodb_old_blocks_time控制。

另外:New SubList也是經過優化的,如果你訪問的是New SubList的前1/4的資料,他是不會被移動到LRU連結串列頭部去的。

當然你可能對什麼是資料區並不瞭解,別急。白日夢將在第13篇文章中同你分享什麼是資料區


五、推薦閱讀

1、談談MySQL中基數是什麼?

2、聊聊什麼是慢查?如何監控?如何排查?

3、對Not Null欄位插入Null值有啥現象?

4、能談談year、date、datetime、time、timestamp的區別嗎?

5、你有沒有搞混查詢快取和Buffer Pool?談談看!



參考:

https://dev.mysql.com/doc/refman/5.7/en/innodb-buffer-pool.html

https://dev.mysql.com/doc/refman/5.7/en/innodb-performance-midpoint_insertion.html

https://dev.mysql.com/doc/refman/5.7/en/innodb-performance-read_ahead.html


關注送書!《Netty實戰》

文章公號號首發!連載中!關注微信公號回覆:“抽獎” 可參加抽?活動


相關文章