Site icon Soul & Shell Blog

MySQL 效能優化 - 淺談 MySQL Buffer Pool 分頁管理機制

資料庫與 Buffer Pool

雖然工作上常用到 MySQL,但是一直沒有機會深入研究,剛好最近要改善 MySQL Server Loading 問題,前天看了一些官方文件,發現 MySQL 對於 Buffer Pool 的設計還蠻有趣的,順便來介紹一下。

目前用的版本是 Ubuntu Server 14.04 內建的 MySQL 5.5,資料庫當然也是使用 InnoDB Engine。資料當然是存在磁碟中,資料庫在工作時需要載入磁碟上的資料到記憶體中,MySQL 資料庫在記憶體的工作區就稱為 Buffer Pool。磁碟空間很容易擴充,但是記憶體就很有限,如何管理有限的記憶體空間,來快速查詢既龐大且放在磁碟中的檔案,就需要設計許多演算法來滿足。

MySQL 資料庫會將實體資料切分成許多集合來儲存,稱為 Page。而我們知道 MySql InnoDB Engine 會將 Disk Page 放到 Buffer Pool (記憶體) 進行資料操作,以增進速度與效率。如果資料不大而你的記憶體又夠多,Buffer Pool 夠大可以放下全部的資料,那大致上不會有什麼問題。但是如果 Buffer Pool 常常需要在 Disk 之間搬移 Page,這些磁碟 I/O 會嚴重影響效能。為了可以有效管理 Buffer Pool 的使用,必須有一套機制來控制哪些 Page 適合從磁碟讀進來 Buffer Pool 進行存取,這個控制機制就是 LRU。

MySQL Buffer Pool LRU 機制

MySQL InnoDB 透過 LRU (Least Recently Used) 演算法來處理這些最近沒用到的 Page,LRU 會把比較舊的 Page 趕出去,這個把 Page 移出的動作稱為 Eviction,好騰出空間來新進的 Page。

在 LRU 的機制下會維護一個列表 (List),把常用的 Page Block 放在前面 (63% = 5/8) 稱為 New Sublists (young),不常用的 Page Block 放在後面 (37% = 3/8) 稱為 Old Sublists,這個分界點稱為 Midpoint,這個分界比例可以透過 innodb_old_blocks_pct 參數來更改設定 (預設為 37%)。LRU 機制下會把不常用 Old Sublists 最尾端的 Page 優先趕出去 (Eviction)。LRU List 如下:

一開始,當有 SQL Query 或進行 read-ahead (MySql Page 預載機制) 時,需要用到的 Page 會先從 Disk 讀進來放在 LRU List 後端的 Old Sublists 前方,如果接著執行 Query,當真正存取到 Page 時,Page 就會被搬到 LRU 前端的 New Sublists,但由於 read-ahead 進來的 Page 不一定會被馬上存取,因此就沒有優先進貴賓室 (New Sublists) 的權利。當 Buffer Pool 不夠用時,MySQL 會將 Old Sublists 最後面的 Page 同步到磁碟 (如果需要的話) 中,好讓 Buffer Pool 有空間從 Disk 讀取新的 Page 進來處理。

read-ahead 是 MySql 自動判斷載入 Page 到 Buffer Pool 的機制,好讓 Buffer Pool 盡可能使用到全部配置的記憶體。載入方式分為 Linear 與 Random 兩種。

上述機制一切聽起來很合理,但是一旦發生一些 Full Table Scan Query 造成把整張資料表讀進 Buffer Pool 進行查詢,之後也不常使用到,那豈不是整個 Buffer Pool 都是新進來的 Page,這些一次性查詢的 Page 污染了整的 Buffer Pool,對於很珍貴的 Buffer Pool 來說是很浪費的。難道其他真正常被使用的 Page Block 只能重讀 Old Sublists 時,第一次存取不會直接進 New Sublists,除非持續在 Old Sublists 停留的時間超過這個 innodb_old_blocks_time 設定值才會被搬移,表示有在存取而沒有被其他新進的 Page 排擠掉,避免一次 Full Table Scan 造成整個 Buffer Pool 被污染,保留了太多只被用到一次的 Page 在 Buffer Pool。

上述提到的 innodb_old_blocks_pct 與 innodb_old_blocks_time 都是可以動態設定,可以在 MySQL 不 shutdown 的狀態下透過 SET GLOBAL 命令來動態調整,會馬上生效,調整時建議透過 show engine innodb status 來分析分頁處理統計資料,對於不同的商業需求與查詢會有不同的設定方式,千萬別從網路上面抄過來直接套用,需求不同反而容易造成反效果。

持續調整與追蹤

說實在的,實務上並不容易確認各種不同的 Query 查詢頻率,導致資料庫實在不容易調整,只能改個設定跑一段時間,透過統計資訊來判斷效果。RDBMS 還真的是博大精深啊,下圖是 MySQL 架構圖,要了解每個元件的時限還真的不容易,下次再繼續介紹 MySQL 其他的運作與優化機制,掰囉~

Exit mobile version