本文主要內容源自官網:https://dev.mysql.com/doc/refman/8.0/en/innodb-buffer-pool.html
感興趣的可以直接閱讀
緩衝池是主內存的一塊區域,在 InnoDB 訪問表和索引數據時會在其中進行高速緩存(可以聯繫 CPU 的高速緩存)。在專用服務器上,通常會把 80% 的內存分配給緩衝池。
緩衝池被分爲多個頁面,這些頁面可能包含多個行。緩衝池使用列表數據結構。使用 LRU (Least Recently Used,最近最少使用)算法的變體將數據從緩存中老化。
緩衝池 LRU 算法
緩衝池 LRU 算法將緩衝池作爲列表進行管理。當緩衝池空間不足,但有新頁面需要添加到緩衝池時,將驅逐最近最少使用的頁面,並將新頁面添加到列表的 Midpoint(中點)。總列表分爲兩個子列表(Sublist):
1. 最前面的是最近訪問過的新頁面(或者叫 young 頁面,年輕頁面)子列表
2. 末尾是最近訪問的舊頁面的子列表
該算法將經常需要訪問的頁面保留在 New Sublist。Old Sublist 包含不常用的頁面,這些頁面是驅逐的候選對象。
通常情況,該算法遵循以下規則:
1. 的 Buffer Pool 用於 Old Sublist
2. 列表的 Midpoint 是 New Sublist 的 Tail 和 Old Sublist 的 Head 相交的邊界。
3. 當 InnoDB 將頁面讀入緩衝池時,它首先將頁面插入 Midpoint。通過用戶觸發的動作(比如 SQL 查詢)或者預讀操作可以對頁面進行讀取。
注意:官方這裏說的讀取(read)並不代表訪問(access),請看下面。
4. 訪問(access)Old Sublist 中的頁面會讓其變得 young,然後將其移至 New Sublist 的 Head。如果是因爲用戶觸發的動作需要讀取頁面,則將立即進行第一次 access,使頁面 young。如果使由於預讀操作而讀取了該頁面,則第一次 access 不會立即訪問,甚至在頁面離開之前都不會發生!
注意:預讀未必會讓頁面變 young。
5. 隨着數據庫的運行,通過將頁面移動到列表的尾部,緩衝池中的頁面將會“老化”。New 和 Old Sublist 都會隨着其他頁面的更新而老化。隨着將頁面插入 Midpoint,舊子列表中的頁面也會老化。最終,未使用的頁面到達子列表的尾部並被逐出。
注意:這裏有個有趣的現象,頁面是先插入到 Midpoint,而且這些頁面插入之後屬於 Old Sublist 的範圍,所以他們很可能會馬上“老化”,唯一讓他們變得 young 的途徑就是 access 一次。
默認情況下,查詢(Query)讀取的頁面會立即移入新的子列表,這意味着它們在緩衝池停留的時間更長。
舉個例子,mysqldump 操作或者 SELECT 不帶 WHERE 子句的語句可能會將大量數據帶入緩衝池,並驅逐出相當多的舊數據,即使加入 New Sublist Head 的新數據可能都不會再被使用了。同樣,預讀線程加載的頁面,且僅僅訪問過一次,那麼也會移至 New Sublist 的開頭。這些情況可能會將常用頁面推到舊的子列表,然後被逐出。
所以,這似乎有個問題,有些頁面命名使用的頻率不如其他頁面,卻可以插在其他頁面前面,甚至逐出其他頁面 ,官方給出了優化此行爲的方法:讓緩衝池的掃描具有“抵抗力”、配置 InnoDB 緩衝池預讀(具體見官網)。