理解Mysql中的Buffer pool


 

Buffer Pool在數據庫里的地位

1、回顧一下Buffer Pool是個什么東西?

數據庫中的Buffer Pool是個什么東西?其實他是一個非常關鍵的組件,數據庫中的數據實際上最終都是要存放在磁盤文件上的,如下圖所示。

 

 

但是我們在對數據庫執行增刪改操作的時候,不可能直接更新磁盤上的數據的,因為如果你對磁盤進行隨機讀寫操作,那速度是相當的慢,隨便一個大磁盤文件的隨機讀寫操作,可能都要幾百毫秒。如果要是那么搞的話,可能你的數據庫每秒也就只能處理幾百個請求了! 在對數據庫執行增刪改操作的時候,實際上主要都是針對內存里的Buffer Pool中的數據進行的,也就是實際上主要是對數據庫的內存里的數據結構進行了增刪改,如下圖所示。

 

 

其實每個人都擔心一個事,就是你在數據庫的內存里執行了一堆增刪改的操作,內存數據是更新了,但是這個時候如果數據庫突然崩潰了,那么內存里更新好的數據不是都沒了嗎? MySQL就怕這個問題,所以引入了一個redo log機制,你在對內存里的數據進行增刪改的時候,他同時會把增刪改對應的日志寫入redo log中,如下圖。

 

 

萬一你的數據庫突然崩潰了,沒關系,只要從redo log日志文件里讀取出來你之前做過哪些增刪改操作,瞬間就可以重新把這些增刪改操作在你的內存里執行一遍,這就可以恢復出來你之前做過哪些增刪改操作了。 當然對於數據更新的過程,他是有一套嚴密的步驟的,還涉及到undo log、binlog、提交事務、buffer pool臟數據刷回磁盤,等等。

2、Buffer Pool的一句話總結

Buffer Pool是數據庫中我們第一個必須要搞清楚的核心組件,因為增刪改操作首先就是針對這個內存中的Buffer Pool里的數據執行的,同時配合了后續的redo log、刷磁盤等機制和操作。

所以Buffer Pool就是數據庫的一個內存組件,里面緩存了磁盤上的真實數據,然后我們的系統對數據庫執行的增刪改操作,其實主要就是對這個內存數據結構中的緩存數據執行的。

 

Buffer Pool這個內存數據結構到底長個什么樣子?

1、如何配置你的Buffer Pool的大小?

我們應該如何配置你的Buffer Pool到底有多大呢? 因為Buffer Pool本質其實就是數據庫的一個內存組件,你可以理解為他就是一片內存數據結構,所以這個內存數據結構肯定是有一定的大小的,不可能是無限大的。 這個Buffer Pool默認情況下是128MB,還是有一點偏小了,我們實際生產環境下完全可以對Buffer Pool進行調整。 比如我們的數據庫如果是16核32G的機器,那么你就可以給Buffer Pool分配個2GB的內存,使用下面的配置就可以了。 [server] innodb_buffer_pool_size = 2147483648 如果你不知道數據庫的配置文件在哪里以及如何修改其中的配置,那建議可以先在網上搜索一些MySQL入門的資料去看看,其實這都是最基礎和簡單的。 我們先來看一下下面的圖,里面就畫了數據庫中的Buffer Pool內存組件

 

 

2、數據頁:MySQL中抽象出來的數據單位

假設現在我們的數據庫中一定有一片內存區域是Buffer Pool了,那么我們的數據是如何放在Buffer Pool中的?

我們都知道數據庫的核心數據模型就是 表+字段+行 的概念,所以大家覺得我們的數據是一行一行的放在Buffer Pool里面的嗎? 這就明顯不是了,實際上MySQL對數據抽象出來了一個數據頁的概念,他是把很多行數據放在了一個數據頁里,也就是說我們的磁盤文件中就是會有很多的數據頁,每一頁數據里放了很多行數據,如下圖所示。

 

 

所以實際上假設我們要更新一行數據,此時數據庫會找到這行數據所在的數據頁,然后從磁盤文件里把這行數據所在的數據頁直接給加載到Buffer Pool里去。 也就是說,Buffer Pool中存放的是一個一個的數據頁,如下圖。

 

 

 

3、磁盤上的數據頁和Buffer Pool中的緩存頁是如何對應起來的?

實際上默認情況下,磁盤中存放的數據頁的大小是16KB,也就是說,一頁數據包含了16KB的內容。 而Buffer Pool中存放的一個一個的數據頁,我們通常叫做緩存頁,因為畢竟Buffer Pool是一個緩沖池,里面的數據都是從磁盤緩存到內存去的。 而Buffer Pool中默認情況下,一個緩存頁的大小和磁盤上的一個數據頁的大小是一一對應起來的,都是16KB。 我們看下圖,我給圖中的Buffer Pool標注出來了他的內存大小,假設他是128MB吧,然后數據頁的大小是16KB。

 

 

4、緩存頁對應的描述信息是什么?

對於每個緩存頁,他實際上都會有一個描述信息,這個描述信息大體可以認為是用來描述這個緩存頁的。 比如包含如下的一些東西:這個數據頁所屬的表空間、數據頁的編號、這個緩存頁在Buffer Pool中的地址以及別的一些雜七雜八的東西。 每個緩存頁都會對應一個描述信息,這個描述信息本身也是一塊數據,在Buffer Pool中,每個緩存頁的描述數據放在最前面,然后各個緩存頁放在后面。所以此時我們看下面的圖,Buffer Pool實際看起來大概長這個樣子 。

 

 

而且這里我們要注意一點,Buffer Pool中的描述數據大概相當於緩存頁大小的5%左右,也就是每個描述數據大概是800個字節左右的大小,然后假設你設置的buffer pool大小是128MB,實際上Buffer Pool真正的最終大小會超出一些,可能有個130多MB的樣子,因為他里面還要存放每個緩存頁的描述數據。

思考

對於Buffer Pool而言,他里面會存放很多的緩存頁以及對應的描述數據,那么假設Buffer Pool里的內存都用盡了,已經沒有足夠的剩余內存來存放緩存頁和描述數據了,此時Buffer Pool里就一點內存都沒有了嗎?還是說Buffer Pool里會殘留一些內存碎片呢? 如果你覺得Buffer Pool里會有內存碎片的話,那么你覺得應該怎么做才能盡可能減少Buffer Pool里的內存碎片呢?

 

在生產環境中,如何基於機器配置來合理設置Buffer Pool?

 

1、生產環境中應該給buffer pool設置多少內存?

今天這篇文章我們接着上一次講解的Buffer Pool的一些內存划分的原理,來給大家最后總結一下,在生產環境中到底應該如何設置Buffer Pool的大小呢。 首先考慮第一個問題,我們現在數據庫部署在一台機器上,這台機器可能有個8G、16G、32G、64G、128G的內存大小,那么此時buffer pool應該設置多大呢? 有的人可能會想,假設我有32G內存,那么給buffer pool設置個30GB得了,這樣的話,MySQL大量的crud操作都是基於內存來執行的,性能那是絕對高! 這么想就大錯特錯了,雖然你的機器有32GB的內存,但是你的操作系統內核就要用掉起碼幾個GB的內存!你的機器上可能還有別的東西在運行!你的數據庫里除了buffer pool是不是還有別的內存數據結構! 所以上面那種想法是絕對不可取的! 如果你胡亂設置一個特別大的內存給buffer,會導致你的mysql啟動失敗的,他啟動的時候就發現操作系統的內存根本不夠用! 所以通常來說,我們建議一個比較合理的、健康的比例,是給buffer pool設置你的機器內存的50%~60%左右 比如你有32GB的機器,那么給buffer設置個20GB的內存,剩下的留給OS和其他人來用,這樣比較合理一些。 假設你的機器是128GB的內存,那么buffer pool可以設置個80GB左右,大概就是這樣的一個規則。

 

2、buffer pool總大小=(chunk大小 * buffer pool數量)的2倍數

接着確定了buffer pool的總大小之后,就得考慮一下設置多少個buffer pool,以及chunk的大小。 此時有一個很關鍵的公式:buffer pool總大小 = (chunk大小 * buffer pool數量) 的倍數 比如默認的chunk大小是128MB,那么此時如果你的機器的內存是32GB,你打算給buffer pool總大小在20GB左右,此時你的buffer pool的數量應該是多少個呢?

假設你的buffer pool的數量是16個,這是沒問題的,那么此時chunk大小 * buffer pool的數量 = 16 * 128MB = 2048MB,然后buffer pool總大小如果是20GB,此時buffer pool總大小就是2048MB的10倍,這就符合規則了。 當然,此時你可以設置多一些buffer pool數量,比如設置32個buffer pool,那么此時buffer pool總大小(20GB)就是(chunk大小128MB * 32個buffer pool)的5倍,也是可以的。 那么此時你的buffer pool大小就是20GB,然后buffer pool數量是32個,每個buffer pool的大小是640MB,然后每個buffer pool包含5個128MB的chunk,算下來就是這么一個結果了。

 

3、一點總結

數據庫在生產環境運行時,必須根據機器的內存設置合理的buffer pool的大小,然后設置buffer pool的數量,這樣可以盡可能的保證你的數據庫的高性能和高並發能力。 在線上運行時,buffer pool是有多個的,每個buffer pool里多個chunk但是共用一套鏈表數據結構,然后執 行crud的時候,就會不停的加載磁盤上的數據頁到緩存頁里來,然后會查詢和更新緩存頁里的數據,同時維護一系列的鏈表結構。 然后后台線程定時根據lru鏈表和flush鏈表,去把一批緩存頁刷入磁盤釋放掉這些緩存頁,同時更新free鏈表。 如果執行crud的時候發現緩存頁都滿了,沒法加載自己需要的數據頁進緩存,此時就會把lru鏈表冷數據區域的緩存頁刷入磁盤,然后加載自己需要的數據頁進來。 整個buffer pool的結構設計以及工作原理,就是上面我們總結的這套東西了,大家只要理解了這個,首先你對MySQL執行crud的時候,是如何在內存里查詢和更新數據的,你就徹底明白了。

接着我們后面繼續探索undo log、redo log、事務機制、事務隔離、鎖機制,這些東西,一點點就把MySQL他的數據更新、事務、鎖這些原理,全部搞清楚了,同時中間再配合穿插一些生產經驗、實戰案例。

 

4、SHOW ENGINE INNODB STATUS

當你的數據庫啟動之后,你隨時可以通過上述命令,去查看當前innodb里的一些具體情況,執行SHOW ENGINE INNODB STATUS就可以了。此時你可能會看到如下一系列的東西:

Total memory allocated xxxx;
Dictionary memory allocated xxx
Buffer pool size xxxx
Free buffers xxx
Database pages xxx
Old database pages xxxx
Modified db pages xx
Pending reads 0
Pending writes: LRU 0, flush list 0, single page 0
Pages made young xxxx, not young xxx
xx youngs/s, xx non-youngs/s
Pages read xxxx, created xxx, written xxx
xx reads/s, xx creates/s, 1xx writes/s
Buffer pool hit rate xxx / 1000, young-making rate xxx / 1000 not xx / 1000
Pages read ahead 0.00/s, evicted without access 0.00/s, Random read ahead 0.00/s
LRU len: xxxx, unzip_LRU len: xxx
I/O sum[xxx]:cur[xx], unzip sum[16xx:cur[0]

下面解釋一下這里的東西,主要講解這里跟buffer pool相關的一些東西。

  1. Total memory allocated,這就是說buffer pool最終的總大小是多少

  2. Buffer pool size,這就是說buffer pool一共能容納多少個緩存頁

  3. Free buffers,這就是說free鏈表中一共有多少個空閑的緩存頁是可用的

  4. Database pages和Old database pages,就是說lru鏈表中一共有多少個緩存頁,以及冷數據區域里的緩存頁數量

  5. Modified db pages,這就是flush鏈表中的緩存頁數量

  6. Pending reads和Pending writes,等待從磁盤上加載進緩存頁的數量,還有就是即將從lru鏈表中刷入磁盤的數量、即將從flush鏈表中刷入磁盤的數量

  7. Pages made young和not young,這就是說已經lru冷數據區域里訪問之后轉移到熱數據區域的緩存頁的數 量,以及在lru冷數據區域里1s內被訪問了沒進入熱數據區域的緩存頁的數量

  8. youngs/s和not youngs/s,這就是說每秒從冷數據區域進入熱數據區域的緩存頁的數量,以及每秒在冷數據區域里被訪問了但是不能進入熱數據區域的緩存頁的數量

  9. Pages read xxxx, created xxx, written xxx,xx reads/s, xx creates/s, 1xx writes/s,這里就是說已經讀取、創建和寫入了多少個緩存頁,以及每秒鍾讀取、創建和寫入的緩存頁數量

  10. Buffer pool hit rate xxx / 1000,這就是說每1000次訪問,有多少次是直接命中了buffer pool里的緩存的

  11. young-making rate xxx / 1000 not xx / 1000,每1000次訪問,有多少次訪問讓緩存頁從冷數據區域移動到了熱數據區域,以及沒移動的緩存頁數量

  12. LRU len:這就是lru鏈表里的緩存頁的數量

  13. I/O sum:最近50s讀取磁盤頁的總數

  14. I/O cur:現在正在讀取磁盤頁的數量

 


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM