共計 2519 個字符,預計需要花費 7 分鐘才能閱讀完成。
本篇內容介紹了“如何理解 MySQL 的 Buffer Pool”的有關知識,在實際案例的操作過程中,不少人都會遇到這樣的困境,接下來就讓丸趣 TV 小編帶領大家學習一下如何處理這些情況吧!希望大家仔細閱讀,能夠學有所成!
前言
buffer pool 是什么
咱們在使用 mysql 的時候,比如很簡單的 select * from table; 這條語句,具體查詢數據其實是在存儲引擎中實現的,大家都知道 mysql 數據其實是放在磁盤里面的,如果每次查詢都直接從磁盤里面查詢,這樣勢必會很影響性能,所以一定是先把數據從磁盤中取出,然后放在內存中,下次查詢直接從內存中來取。但是一臺機器中往往不是只有 mysql 一個進程在運行的,很多個進程都需要使用內存,所以 mysql 中會有一個專門的區域來處理這些數據,這個專門為 mysql 準備的區域,就叫 buffer pool。
buffer pool 的工作流程
咱們以查詢語句為例 1: 在查詢的時候會先去 buffer pool(內存) 中看看有沒有對應的數據頁,如果有的話直接返回 2: 如果 buffer pool 中沒有對應的數據頁,則會去磁盤中查找,磁盤中如果找到了對應的數據,則會把該頁的數據直接 copy 一份到 buffer pool 中返回給客戶端 3: 下次有同樣的查詢進來直接查找 buffer pool 找到對應的數據返回即可。
大家看到這里相信應該對 buffer pool 有了個大概的認識,有沒有感覺有點緩存的感覺,當然 buffer pool 可沒有緩存那么簡單,內部結構還是比較復雜的,不過沒關系,咱們繼續往下看。
buffer pool 數據管理
數據管理的基本單位
buffer pool 畢竟是一種內存管理,數據當然不是按照一條一條的 sql 語句來管理的,而是按照數據頁來管理的,innodb 引擎默認的數據頁是 16kb,而 buffer pool 啟動的時候是默認的 128M,所以是有 8192 個數據頁的。而磁盤的數據管理也是用數據頁為單位來管理的,所以每次查找數據的時候,先請求 buffer pool,buffer pool 中沒有的話會到磁盤中找到對應的數據頁,然后 copy 到 buffer pool 中給客戶端返回。
free 鏈表
正常情況下,buffer pool 肯定是從第一個數據頁,不斷的往后填充的,一個一個的往后寫入, 每次直接在后面追加就可以了。如下圖 (黃色部分表示已經寫入數據)
但是實際生產環境中,并不是這樣的,我們不光有查詢操作,還有刪除,修改等操作,而且已經寫入 buffer pool 的數據不一定是始終有價值的,有一些數據是不需要的,需要釋放對應的數據頁的,所以就會造成 buffer pool 的數據其實是這種情況,間斷不連續的。
在這種情況下該如何去找到有效的空閑的數據頁空間來存儲數據呢? 最直觀的方法就是從第一個頁遍歷的一個一個的往后找,找到空閑的數據頁即可,這種方法倒是可行,但是非常影響效率,所以 mysql 在處理這種問題用上了 free 鏈表的方式來管理空閑的數據頁。
大家可以看一看 free 鏈表的結構
free 鏈表有一個基節點,記錄了該 free 鏈表的唯一標志,該鏈表的尾節點地址,以及鏈表的總長度
基節點后面會有很多的控制塊,控制塊本身很小,只是存儲了指向空閑數據頁的指針而已,所以 buffer pool 在尋找空閑數據頁的時候直接用 free 鏈表可以直接找到。
只要有一頁數據空閑出來之后,直接把該數據頁的地址追加到 free 鏈表即可。
flush 鏈表
當然只是用 free 鏈表是解決不了所有問題的,比如:我們在執行 update table test set field_a = 1; 的時候,我們是先修改 buffer pool 里面對應的數據頁,然后再更新磁盤中對應的數據頁的,(當然這里會涉及到一個數據一致性的問題,mysql 是用 redo log 解決的,這個不在咱們這篇文章的討論范圍之內) 我們把 buffer pool 中對應修改的數據頁同步修改到磁盤的時候,這個過程稱之為 刷臟 , 刷臟是有一定策略的,可以用
select @@innodb_flush_log_at_trx_commit;
來查看刷臟策略
我們一般都不會設置實時寫,這樣很影響性能,所以一般都是延遲寫的,那么就會引發一個問題,mysql 是如何在 buffer pool 中找到被修改過的臟數據的呢? 這里咱們就用上了 flush 鏈表了,其實和 free 鏈表比較像
flush 鏈表上面維護的都是臟數據頁的指針。刷臟的時候直接遍歷 flush 鏈表去刷臟就可以了。
lru 鏈表
buffer pool 是有一定空間限制的,默認是 128M,總會有空間塞滿的時候的,所以數據頁是有淘汰機制的,淘汰機制就是 lru(最近最少使用)。
lru 原理其實也很簡單,使用到過的數據頁,直接移動到鏈表的頭部,然后在 buffer pool 滿了之后直接淘汰掉鏈表尾部的數據頁就可以了。
lru 鏈表的優化
其實簡單的 lru 鏈表是存在一定的問題的,比如咱們在工作過程中,可能會用上 select * from test 這樣的語句來進行一些刷數據等需求,如果 test 表是非常大的,很有可能一下子把 buffer pool 占滿,把之前的數據頁全部都淘汰掉,然后其余的數據在線上業務正常執行的時候,又會回來重新把之前 select * from test 占用的數據頁重新慢慢淘汰掉,這一來一去是非常影響線上的性能的。
所以鑒于以上所在的問題,mysql 的 buffer pool 是在 lru 的基礎上進行了一些優化的。
buffer pool 的 lru 鏈表把數據分為了熱數據塊和冷數據塊,比例大概 5:3 的樣子,每次新的數據頁寫入都會寫入冷數據區。
但是如果這樣的話那么熱數據區永遠都不會有數據,所以冷數據區寫入的時候會另外記錄上寫入的時間,下次訪問該數據區的時候如果時間間隔大于 1s, 那么就會放入熱數據區,這樣就不會淘汰掉大量的無辜數據。所以我們在執行 select * from test 這種語句刷新腳本的時候,只會占用冷數據的空間,而不會影響到熱數據。
“如何理解 MySQL 的 Buffer Pool”的內容就介紹到這里了,感謝大家的閱讀。如果想了解更多行業相關的知識可以關注丸趣 TV 網站,丸趣 TV 小編將為大家輸出更多高質量的實用文章!