1. 程式人生 > >你知道MySQL的LRU連結串列嗎?

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

相信大家對LRU連結串列是不陌生的,算是一種基礎的資料結構! LRU:Least Recently Used
### 一、簡述傳統的LRU連結串列 LRU:Least Recently Used 相信大家對LRU連結串列是不陌生的,它算是一種基礎的資料結構吧,而且想必面試時也被問到過什麼是LRU連結串列,甚至是讓你手寫一個LRU連結串列。 如果你讀了上一篇:[你有沒有搞混查詢快取和BufferPool?談談看!](http://mp.weixin.qq.com/s?__biz=MzI5MDg4ODEzOA==&mid=2247483817&idx=1&sn=d19e5ce05cd9520d1becd7b6afa5e0be&chksm=ec18410cdb6fc81a2bc86184427536832c8f8aff1daae1d1966afa0ff1af667d292ffe43a310&scene=21#wechat_redirect) 想必你已經知道了MySQL的Buffer Pool機制以及MySQL組織資料的最小單位是資料頁。並且你也知道了 資料頁在Buffer Pool中是以LRU連結串列的資料結構組織在一起的。 其實所謂的LRU連結串列本質上就是一個雙向迴圈連結串列,如下圖: ![](https://img2020.cnblogs.com/blog/1496926/202011/1496926-20201116094757283-1443963307.jpg) 下面我們結合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連結串列長下面這樣。 ![](https://img2020.cnblogs.com/blog/1496926/202011/1496926-20201116094757876-1680665456.png) LRU連結串列被MidPoint分成了New Sublist和Old Sublist兩部分。 其中New Sublist大概佔比5/8,Old Sublist佔比3/8。 New Sublist儲存著young page,而Old Sublist儲存著Old Page。 我們可以通過如下的方式檢視MidPoint的預設值。 ![](https://img2020.cnblogs.com/blog/1496926/202011/1496926-20201116094758373-89127857.png) > 使用者可以根據自己的業務動態的調整這個引數! **這其實是一種冷熱資料分離設計思想。他相對於傳統的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中基數是什麼?](http://mp.weixin.qq.com/s?__biz=MzI5MDg4ODEzOA==&mid=2247483722&idx=1&sn=4d1e1eb5e4f97c8a91898195823eb26b&chksm=ec1841efdb6fc8f9429609c18270fd421c61e442ebc893c4fa00ea29df12c73748e1f64ed54e&scene=21#wechat_redirect) [2、聊聊什麼是慢查?如何監控?如何排查?](http://mp.weixin.qq.com/s?__biz=MzI5MDg4ODEzOA==&mid=2247483764&idx=1&sn=332b5b05d931b06a64ab07982bfab794&chksm=ec1841d1db6fc8c732c1c78fce4f9027305fd2590871038817f8386b5fc0b4a3e296b4970d30&scene=21#wechat_redirect) [3、對Not Null欄位插入Null值有啥現象?](http://mp.weixin.qq.com/s?__biz=MzI5MDg4ODEzOA==&mid=2247483766&idx=1&sn=0f53c86b270183e24134b8b3eac71cd6&chksm=ec1841d3db6fc8c553e882d33c2cfc83bc8fc7d5046fbc7ee147bfff3403ec36a1c24094067a&scene=21#wechat_redirect) [4、能談談year、date、datetime、time、timestamp的區別嗎?](http://mp.weixin.qq.com/s?__biz=MzI5MDg4ODEzOA==&mid=2247483802&idx=1&sn=0714dea1bf86ea9f1e44b0eb377b1d1e&chksm=ec18413fdb6fc829fb1285d9142f8f8daccc35b4cb5de47c0f9ef2d5efb5fc8db1bc8f6d5a08&scene=21#wechat_redirect) [5、你有沒有搞混查詢快取和Buffer Pool?談談看!](http://mp.weixin.qq.com/s?__biz=MzI5MDg4ODEzOA==&mid=2247483817&idx=1&sn=d19e5ce05cd9520d1becd7b6afa5e0be&chksm=ec18410cdb6fc81a2bc86184427536832c8f8aff1daae1d1966afa0ff1af667d292ffe43a310&scene=21#wechat_redirect)

參考: 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實戰》

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