緩衝池是主存儲器中的一個區域,在訪問 table 和索引數據時 InnoDB 會對其進行緩存。緩衝池允許直接從記憶體中訪問頻繁使用的數據,從而加快處理速度。在專用伺服器上,通常將高達 80% 的物理記憶體分配給緩衝池。 ...
緩衝池是主存儲器中的一個區域,在訪問 table 和索引數據時InnoDB
會對其進行緩存。緩衝池允許直接從記憶體中訪問頻繁使用的數據,從而加快處理速度。在專用伺服器上,通常將高達 80% 的物理記憶體分配給緩衝池。
為了高效處理大量讀取操作,緩衝池被劃分為可以容納多行數據的頁面。為了有效管理緩存,緩衝池被實現為頁面的鏈接列表;通過 LRU(least recently used)演算法的變體將很少使用的數據從緩存中淘汰出去。
瞭解如何利用緩衝池將頻繁訪問的數據保留在記憶體中是MySQL調優的重要方面之一。
緩衝池 LRU 演算法
緩衝池使用一種最近最少使用(LRU)演算法的變體作為列表進行管理。當需要空間以將新頁面添加到緩衝池時,最近最少使用的頁面會被移除,並將新頁面添加到列表的中間。這種中點插入策略將列表視為兩個子列表:
- 在前面是最近訪問過的新("young")頁面的子列表;
- 在尾部是最近較少被訪問的舊("old")頁面子列表。
緩衝池列表如下圖所示:
該演算法將頻繁使用的頁面保留在新頁面子列表中。舊頁面子列表則包含較少被使用的頁面,這些頁面是可能被淘汰(eviction)的候選頁面。
預設情況下,演算法運行如下:
- 緩衝池的 3/8 專門用於舊頁面子列表。
- 列表的中點是新頁面子列表的尾部與舊頁面子列表的頭部相遇的邊界位置。
- 當
InnoDB
將一個頁面讀入緩衝池時,它最初會插入到中點位置(舊頁面子列表的頭部)。一個頁面可以被讀取,因為它是用戶發起的操作(例如 SQL 查詢)所必需的,或者是InnoDB
自動執行的預讀(read-ahead)操作的一部分。 - 訪問舊頁面子列表中的一個頁面會使其變為"young",並將其移動到新頁面子列表的開頭。如果頁面由於用戶發起的操作而被讀取,則將立即進行首次訪問,並且頁面會被標記為"young"。如果頁面是由於預讀操作而被讀取,則第一次訪問不會立即發生,並且在該頁面被淘汰之前可能根本不會發生。
- 隨著資料庫的運行,緩衝池中未被訪問的頁面會通過向列表的尾部移動而"老化"。新頁面子列表和舊頁面子列表中的頁面都會隨著其他頁面的更新而老化。舊頁面子列表中的頁面也會隨著在中點插入頁面而老化。最終,一個長時間未被使用的頁面會到達舊頁面子列表的尾部並被淘汰。
預設情況下,通過查詢讀取的頁面會立即移動到新頁面子列表中,這意味著它們在緩衝池中停留的時間更長。例如,對於執行mysqldump操作或不帶WHERE
子句的SELECT
語句進行的表掃描,可能會將大量數據帶入緩衝池,並淘汰相同數量的較舊數據,即使新數據永遠不會再次使用。同樣地,由預讀取後臺線程載入且僅訪問一次的頁面會移動到新頁面子列表的開頭。這些情況會將頻繁使用的頁面推入舊頁面子列表,使其面臨淘汰的風險。關於優化這種行為的信息,請參閱"使緩衝池具有掃描抵抗力"和"配置 InnoDB 緩衝池預取(預讀)"。
InnoDB
標準監視器(Standard Monitor)的輸出在BUFFER POOL AND MEMORY
部分中包含了幾個與緩衝池 LRU 演算法操作有關的欄位。有關詳細信息,請參閱使用 InnoDB 標準監視器監控緩衝池。
緩衝區配置
您可以配置緩衝池的各個方面以提高性能。
- 理想情況下,您應該將緩衝池的大小設置為儘可能大的值,同時確保為伺服器上的其他進程留有足夠的記憶體,以避免過多的頁面交換(paging)。緩衝池越大,
InnoDB
就更像是一個記憶體資料庫,從磁碟讀取一次數據,然後在後續讀取從記憶體中訪問數據。有關詳細信息,請參閱"配置 InnoDB 緩衝池大小"。 - 在具有足夠記憶體的64位系統上,可以將緩衝池分成多個部分,以最大程度地減少併發操作之間對記憶體結構的爭用。有關詳細信息,請參閱"配置多個緩衝池實例"。
- 您可以將頻繁訪問的數據保留在記憶體中,而不受會將大量不經常訪問的數據帶入緩衝池的操作突然活動的影響。有關詳細信息,請參閱"使緩衝池具有掃描抵抗力"。
- 您可以控制何時以及如何執行預讀請求,以非同步方式將頁面預取到緩衝池中,從而期望這些頁面很快會被使用。有關詳細信息,請參閱"配置 InnoDB 緩衝池預取(預讀)"。
- 您可以控制何時進行後臺刷新,以及是否根據工作負載動態調整刷新速率。有關詳細信息,請參閱"配置緩衝池刷新"。
- 您可以配置
InnoDB
保存當前的緩衝池狀態的方式,以避免伺服器重新啟動後的漫長預熱時間。有關詳細信息,請參閱"保存和恢復緩衝池狀態"。
使用 InnoDB 標準監視器監控緩衝池
可以使用SHOW ENGINE INNODB STATUS訪問InnoDB
標準監視器輸出提供的有關緩衝池操作的指標。緩衝池指標位於InnoDB
標準監視器輸出的BUFFER POOL AND MEMORY
部分:
----------------------
BUFFER POOL AND MEMORY
----------------------
Total large memory allocated 2198863872
Dictionary memory allocated 776332
Buffer pool size 131072
Free buffers 124908
Database pages 5720
Old database pages 2071
Modified db pages 910
Pending reads 0
Pending writes: LRU 0, flush list 0, single page 0
Pages made young 4, not young 0
0.10 youngs/s, 0.00 non-youngs/s
Pages read 197, created 5523, written 5060
0.00 reads/s, 190.89 creates/s, 244.94 writes/s
Buffer pool hit rate 1000 / 1000, young-making rate 0 / 1000 not
0 / 1000
Pages read ahead 0.00/s, evicted without access 0.00/s, Random read
ahead 0.00/s
LRU len: 5720, unzip_LRU len: 0
I/O sum[0]:cur[0], unzip sum[0]:cur[0]
下表描述了InnoDB
標準監視器報告的緩衝池指標。
註:InnoDB
標準監視器輸出中提供的每秒平均值是基於自上次列印InnoDB
標準監視器輸出以來經過的時間計算的。
InnoDB 緩衝池指標如下表所示:
Name | Description |
---|---|
Total memory allocated | 為緩衝池分配的總記憶體(以位元組為單位)。 |
Dictionary memory allocated | 為InnoDB 數據字典分配的總記憶體(以位元組為單位)。 |
Buffer pool size | 分配給緩衝池的頁面總大小。 |
Free buffers | 緩衝池空閑列表的頁面總大小。 |
Database pages | 緩衝池 LRU 列表的頁面總大小。 |
Old Database pages | 緩衝池舊 LRU 子列表的頁面總大小。 |
Modified db pages | 當前在緩衝池中修改的頁面數。 |
Pending reads | 等待讀入緩衝池的緩衝池頁面數。 |
Pending writes LRU | 從 LRU 列表底部等待寫入緩衝池中舊臟頁的數量。 |
Pending writes flush list | 檢查點期間要刷新的緩衝池頁面數。 |
Pending writes single page | 緩衝池中暫掛的獨立頁面寫入數。 |
Pages made young | 緩衝池 LRU 列表中變年輕的頁面總數(移至“新”頁面的子列表的開頭)。 |
Pages made not young | 緩衝池 LRU 列表中沒有變年輕的頁面總數(保留在“舊”頁面子列表中沒有年輕的頁面)。 |
youngs/s | 在緩衝池 LRU 列表中,平均每秒訪問舊頁面並使其變為年輕頁面的次數。有關更多信息,請參閱此表格後面的註釋。 |
non-youngs/s | 在緩衝池 LRU 列表中,平均每秒訪問舊頁面並未導致頁面變為年輕頁面的次數。有關更多信息,請參閱此表格後面的註釋。 |
Pages read | 從緩衝池讀取的頁面總數。 |
Pages created | 在緩衝池中創建的頁面總數。 |
Pages written | 從緩衝池寫入的頁面總數。 |
reads/s | 平均每秒讀取的緩衝池頁面數。 |
creates/s | 平均每秒創建的緩衝池頁面數。 |
writes/s | 平均每秒緩衝池頁面寫入數。 |
Buffer pool hit rate | 從緩衝池讀取的頁面與從磁碟存儲讀取的頁面之間的緩衝池頁面命中率。 |
young-making rate | 頁面訪問導致頁面變為年輕頁面的平均命中率。有關更多信息,請參閱此表格後面的註釋。 |
not (young-making rate) | 頁面訪問未使頁面變年輕的平均命中率。有關更多信息,請參見此表格後面的註釋。 |
Pages read ahead | 平均每秒的預讀操作次數。 |
Pages evicted without access | 平均每秒從緩衝池中淘汰而被訪問的頁面數量。 |
Random read ahead | 平均每秒隨機預讀操作次數。 |
LRU len | 緩衝池 LRU 列表的頁面總大小。 |
unzip_LRU len | 緩衝池 unzip_LRU 列表的長度(以頁面為單位)。 |
I/O sum | 訪問的緩衝池 LRU 列表頁面總數。 |
I/O cur | 當前間隔內訪問的緩衝池 LRU 列表頁面總數。 |
I/O unzip sum | 已訪問的緩衝池 unzip_LRU 列表頁面的總數。 |
I/O unzip cur | 當前時間間隔內已訪問的緩衝池 unzip_LRU 列表頁面的總數。 |
Notes:
-
年輕頁面生成速率
youngs/s
指標僅適用於舊頁面。它基於頁面的訪問次數而不是頁面數計算。對於給定頁面,可能會有多次訪問,所有訪問都會被計算在內。如果在沒有進行大規模掃描的情況下youngs/s
非常低,則可能需要減少延遲時間或增加用於舊子列表的緩衝池百分比。增加百分比會使舊子列表變大,因此需要更長的時間才能將該子列表中的頁面移動到尾部,從而增加這些頁面再次被訪問併成為年輕頁面的可能性。請參閱“使緩衝池抗掃描”。 -
非年輕頁面生成速率
non-youngs/s
指標僅適用於舊頁面,它基於頁面的訪問次數而不是頁面數計算。對於給定頁面,可能會有多次訪問,所有訪問都會被計算在內。如果在執行大型表掃描(以及較高的youngs/s
)時沒有看到更高的非年輕頁面生成速率值non-youngs/s
,請增加延遲值。請參閱“使緩衝池抗掃描”。 -
年輕頁面生成率
young-making
考慮了所有緩衝池頁面的訪問,而不僅僅是舊子列表中頁面的訪問。年輕頁面生成率young-making
和非年輕頁面生成率non-youngs/s
通常不會累加到整體緩衝池命中率上。在舊子列表中的頁面命中會導致頁面移動到新子列表,但是新子列表中的頁面命中只有當它們距離列表頭部一定距離時才會移動到列表頭部。 -
非(年輕頁面生成率)
not (young-making rate)
是指由於未達到由innodb_old_blocks_time定義的延遲時間,或由於新子列表中的頁面命中未導致頁面移動到頭部,而導致頁面訪問未使頁面變為年輕頁面的平均命中率。此率考慮了所有緩衝池頁面的訪問,而不僅僅是舊子列表中頁面的訪問。
緩衝池伺服器狀態變數和INNODB_BUFFER_POOL_STATS表提供了許多與InnoDB
Standard Monitor 輸出中相同的緩衝池指標。有關更多信息,請參閱示例“查詢INNODB_BUFFER_POOL_STATS table”。
註:原文來自 MySQL 5.7 官方文檔,閱讀 MySQL 中文文檔時有些語句理解不順暢,便結合中文文檔使用 ChatGPT 進行了翻譯,如有不正請指出。