MySQL源碼學習(二) Buffer Pool
Mysql源碼學習(二) Buffer Pool

Buffer Pool是innodb的核心組件之一,所有數(shù)據(jù)的讀取,都會先放到Buffer Pool中,再讀取出來,而不是直接讀取datafile文件。那么對于類似筆者這樣的新手來說,從哪里開始閱讀buffer pool的代碼呢?在源代碼中,幾乎搜不到”buffer pool”這樣的關鍵字?
1.?????? 從buf_pool_t開始
在Mysql代碼庫的src/storage/innobase/include目錄下,大家可以找到buf0buf.h頭文件,這里就是存放buffer pool核心管理結構的地方。在頭文件中, struct buf_pool_t這個定義,就是這個關鍵結構體。大家可以其中看到一個一個熟悉關鍵字:”LRU”,沒錯,大名鼎鼎的LRU鏈表就在這其中,但還有其他很多的字段,在這里不一一展開,后面會通過與LRU鏈表最相關的幾個鏈表逐一展開。
2.?????? 6大鏈表
圖1 Buffer Pool 6大鏈表
如圖1所示,buf_pool_t結構體中的6個關鍵字段: free, LRU, unzip_LRU, flush_list, zip_clean, zip_free分別對應了6大鏈表。其中:
l? free
它是在系統(tǒng)初始化階段bufpool進行初始化后唯一顯式調用鏈表初始化函數(shù)進行init操作的唯一bufpool鏈表。系統(tǒng)內的第一個LRU鏈表塊,必然是從free鏈表中獲取到的,當flush模塊臟頁刷新完成,LRU鏈表節(jié)點就會被清除或者移動到LRU鏈表結尾等待清除,清除LRU之后的節(jié)點仍舊是回歸到free鏈表內。
l? LRU
當有LRU鏈表為空時,必然從free鏈表獲取空閑節(jié)點,并進行異步IO讀將頁讀入bufpool,并加入LRU鏈表,LRU鏈表長度過大的情況下,會進行尾部刷新,刷新失敗會進行更徹底的直接通過LRU進行臟頁刷新(BUF_FLUSH_LRU方式),flush鏈表節(jié)點得到釋放臟頁完成刷新,并同時把LRU鏈表的臟塊也完成移除
l? Unzip_LRU
本鏈表實際是LRU鏈表的一個子集,在壓縮頁控制塊(buf_page_struct)中的壓縮頁需要進行解壓縮以進行各種記錄級讀寫操作時,該鏈表將發(fā)揮作用,因此可以說,插入到了unzip_LRU鏈表就一定頁在LRU鏈表中,反之則未必。
l? Flush_list
l? zip_clean
本鏈表以目前的源代碼來看僅用于調試功能。
l? zip_free
bufpool6大鏈表中最特殊的一個,鏈表的根節(jié)點可以看做“是一個指針數(shù)組”,伙伴系統(tǒng)的精髓就在于按照2的倍數(shù)進行緊鄰內存塊的合并和拆分,進而達到高效管理、代碼復雜度低的效果。這個指針數(shù)組按照塊大小實際包含4層,1024,2048,4096和8192,每一層基結點只管理同類大小的塊。
從圖我們可以看出,LRU是整個Buffer Pool的核心,因此我們以LRU為線索展開說明,zip_clean和zip_free較為特殊,本文暫不做詳細說明,
3.?????? LRU分配
從LRU和free的說明可以知道,LRU都是從free鏈表中獲得真正的內存空間的。分配的函數(shù)入口就在:storage/innobase/buf目錄下的buf0lrn.cc文件中的 buf_LRU_get_free_block函數(shù)
圖2 buf_LRU_get_free_block主要流程
圖2流程中標志了分配的整個流程,但并不是所有情況下,都會完全執(zhí)行這個流程,例如:當free鏈表仍未被使用完時,執(zhí)行完buf_LRU_get_free_only后就會返回。如果free鏈表已被用完,或者buf_LRU_get_free_only返回為NULL,則會執(zhí)行buf_LRU_scan_and_free_block,從已有的LRU列表中找到一個block然后釋放。buf_LRU_scan_and_free_block會在第4小節(jié)中進行闡述
l? buf_LRU_get_free_only
返回一個空閑的block,這個block來自于free鏈表,如果free鏈表為空(內存耗盡),則返回NULL
圖3 buf_LRU_get_free_only函數(shù)主體
從圖3中我們可以看到,首先會從free鏈表的頭部開始便利,找到一個可用的block,并把block的狀態(tài)設置為BUF_BLOCK_READY_FOR_USE的狀態(tài),然后返回。
圖4 若無法從free鏈表中獲取到block,則先釋放一個LRU鐘的block
這里不得不提一下buf_LRU_get_free_block中的這段代碼,如果是第一次進行掃描,則會從LRU的尾部開始掃描(LRU的特點,這里可以提升效率),但是如果不是第一次掃描,則會掃描整個LRU。但是如果掃描了一次,還是沒找到呢?看下圖
圖5 強制刷新一個page,并將page放到free鏈表中
如果掃描超過1次沒有找到能釋放的LRU,則會讓thread sleep一段時間,等待page clean刷一些數(shù)據(jù)到磁盤上,然后會強制執(zhí)行一次刷新,將1個page刷到磁盤上,然后再放到free鏈表中。這里要注意,LRU釋放掉一個page后,并不會直接使用這個page,而是將它先放回free鏈表,然后再按正常流程拿過來。
4.?????? LRU釋放
圖6 LRU釋放流程
LRU的釋放,可以看到LRU的釋放,會從unzip_LRU鏈表和common_LRU鏈表中進行釋放。unzip_LRU我們上面已經介紹,那么common_LRU是什么呢?其實就是LRU本身。另外從這里也可以看出,unzip_LRU就是LRU的一個子集,釋放unzip_LRU就是釋放LRU本身。
我們再進入buf_LRU_free_from_common_LRU_list函數(shù),我們可以看到:
圖7 buf_LRU_free_from_common_LRU_list函數(shù)主體
所謂釋放LRU,是必須要保證這個page是clean的,如果page不是clean的,就需要刷臟。這也是為什么在分配過程中,有可能出現(xiàn)找不到能釋放的page的原因。
更深入的釋放流程涉及到buffer pool的內存管理,我們會在下次再進行深入探討
RDS-MYSQL 云數(shù)據(jù)庫 MySQL RDS MySQL MySQL
版權聲明:本文內容由網(wǎng)絡用戶投稿,版權歸原作者所有,本站不擁有其著作權,亦不承擔相應法律責任。如果您發(fā)現(xiàn)本站中有涉嫌抄襲或描述失實的內容,請聯(lián)系我們jiasou666@gmail.com 處理,核實后本網(wǎng)站將在24小時內刪除侵權內容。
版權聲明:本文內容由網(wǎng)絡用戶投稿,版權歸原作者所有,本站不擁有其著作權,亦不承擔相應法律責任。如果您發(fā)現(xiàn)本站中有涉嫌抄襲或描述失實的內容,請聯(lián)系我們jiasou666@gmail.com 處理,核實后本網(wǎng)站將在24小時內刪除侵權內容。