日韩性视频-久久久蜜桃-www中文字幕-在线中文字幕av-亚洲欧美一区二区三区四区-撸久久-香蕉视频一区-久久无码精品丰满人妻-国产高潮av-激情福利社-日韩av网址大全-国产精品久久999-日本五十路在线-性欧美在线-久久99精品波多结衣一区-男女午夜免费视频-黑人极品ⅴideos精品欧美棵-人人妻人人澡人人爽精品欧美一区-日韩一区在线看-欧美a级在线免费观看

歡迎訪問 生活随笔!

生活随笔

當(dāng)前位置: 首頁 > 运维知识 > 数据库 >内容正文

数据库

[MySQL 源码] 从buffer pool中获取空闲block流程

發(fā)布時間:2025/3/19 数据库 14 豆豆
生活随笔 收集整理的這篇文章主要介紹了 [MySQL 源码] 从buffer pool中获取空闲block流程 小編覺得挺不錯的,現(xiàn)在分享給大家,幫大家做個參考.
當(dāng)我們將一個page讀入內(nèi)存時,需要先為其分配一個block,從buffer pool中獲取。入口函數(shù)為buf_LRU_get_free_block 之前在http://mysqllover.com/?p=303有簡要介紹,這里詳細(xì)看看,當(dāng)然,跟最近博客的主題一樣,我們還是主要針對壓縮表來分析。
以下分析基于Percona Server 5.5.18
buf_LRU_get_free_block loop: 1.block = buf_LRU_get_free_only(buf_pool) 首先從buf_pool->free鏈表尾部讀取,如果有空閑頁,則將其從buf_pool->free中移除,設(shè)置bpage->state=BUF_BLOCK_READY_FOR_USE,然后返回 上述流程需要加buf_pool->free_list_mutex鎖
2.如果1獲得了一個block,還需要重置壓縮頁描述符block->page.zip為0,然后直接返回
3.如果在buf_pool->free上沒有block,則從buf_pool->LRU或unzip_LRU的尾部開始掃描,嘗試找一個空閑block. freed = buf_LRU_search_and_free_block(buf_pool, n_iterations); //n_iterations是一個計數(shù)器,表示嘗試釋放但失敗的次數(shù)。 A.持有buf_pool->LRU_list_mutex鎖 B.首先,嘗試從buf_pool->unzip_LRU上釋放block,這種情況下不會釋放壓縮頁數(shù)據(jù) freed = buf_LRU_free_from_unzip_LRU_list(buf_pool, n_iterations, have_LRU_mutex);

>>判斷是否從unzip_LRU上驅(qū)逐block

理論上講,從buf_pool->unzip_LRU上應(yīng)該更容易獲得一個block,因為我們可以選擇一個臟塊(只驅(qū)逐解壓頁),但當(dāng)我們嘗試5次還是沒有找到時,則直接返回到正常的驅(qū)逐block的邏輯,即從LRU上獲取

另外也有函數(shù)buf_LRU_evict_from_unzip_LRU用來判斷是否從unzip_LRU上驅(qū)逐block,其判斷邏輯如下:

>>>需要持有buf_pool->LRU_list_mutex

>>>如果buf_pool->unzip_LRU長度為0 ,返回FALSE

>>>如果buf_pool->unzip_LRU小于buf_pool->LRU的十分之一,返回FALSE

>>>如果buf_pool->freed_page_clock == 0,表示之前沒有進(jìn)行過任何block驅(qū)逐,默認(rèn)假設(shè)工作負(fù)載為disk bound,返回TRUE

freed_page_clock是一個序列號,用來計數(shù)從LRU尾部移除的block數(shù),可以不加鎖讀取該變量

>>>計算最近的平均IO量

? ? io_avg = buf_LRU_stat_sum.io / BUF_LRU_STAT_N_INTERVAL

? ? ? ? + buf_LRU_stat_cur.io;

最近50秒的平均IO+當(dāng)前的IO,獲得最近的平均IO負(fù)載

? ? unzip_avg = buf_LRU_stat_sum.unzip / BUF_LRU_STAT_N_INTERVAL

? ? ? ? + buf_LRU_stat_cur.unzip;

最近50秒平均解壓page的次數(shù)+當(dāng)前解壓page的次數(shù),獲得最近每秒平均解壓page次數(shù)


我們對io_avg進(jìn)行加權(quán)(BUF_LRU_IO_TO_UNZIP_FACTOR),依次判斷是IO-BOUND還是CPU-BOUND

當(dāng)unzip_avg <= io_avg * BUF_LRU_IO_TO_UNZIP_FACTOR時,表示這是IO-Bound的,返回TRUE

當(dāng)unzip_avg ?> ?io_avg * BUF_LRU_IO_TO_UNZIP_FACTOR時,表示這是CPU-Bound的,返回FALSE。

BUF_LRU_IO_TO_UNZIP_FACTOR是一個宏,值為50。這是一個hard code值,但正如bug#64181所提到的,快速存儲例如SSD可能并不適合這樣的加權(quán),因為SSD相對傳統(tǒng)硬盤具有更快的IO速度。

當(dāng)從buf_LRU_evict_from_unzip_LRU返回值為false時,則從LRU掃描,如果為true,則嘗試從unzip_lru掃描

>>計算在unzip中的最大掃描距離

? ? distance = 100 + (n_iterations

? ? ? ? ? ? ? * UT_LIST_GET_LEN(buf_pool->unzip_LRU)) / 5

? ? ? ? ? ? ? ? ? ? 其中n_iterations<5

>>從buf_pool->unzip_LRU尾部開始掃描,滿足如下條件可以進(jìn)行驅(qū)逐

buf_block_get_state(block) == BUF_BLOCK_FILE_PAGE;

block->in_unzip_LRU_list;

block->page.in_LRU_list;

然后從unzip_lru上釋放block:

freed = buf_LRU_free_block(&block->page, FALSE, have_LRU_mutex);

>>>檢查block是否被pin住(buffer-fixed or I/O-fixed),是的話直接返回false

>>>如果表正在被刪除(bpage->space_was_being_deleted) 并且bpage->oldest_modification !=0時調(diào)用buf_flush_remove(bpage)從flush list上刪除該block

其中bpage->oldest_modification記錄了修改該block,但尚未刷入磁盤的日志記錄起始LSN。值為0表示所有的修改都刷入了磁盤。

>>>分配一個page描述符(buf_page_t)

b = buf_page_alloc_descriptor();

然后將bpage拷貝到b中

?memcpy(b, bpage, sizeof *b);

這里bpage->zip.data的指針也被拷貝到b,因此可以通過b訪問壓縮頁,而在隨后將bpage->zip.data置為NULL

>>>從LRU和Page hash上移除page

buf_LRU_block_remove_hashed_page(bpage, zip)

當(dāng)前流程的zip為false,表示不釋放壓縮page

|–>buf_LRU_remove_block(bpage);

|–>從buf_pool->page_hash中移除

這里zip參數(shù)為false,因此無需釋放壓縮頁數(shù)據(jù)。但如果從LRU釋放,這里可能會成為瓶頸(buf_buddy_free做碎片整理)

>>>將b插入到buf_pool->page_hash和buf_pool->LRU中。

>>>移除可能存在的adaptive hash index記錄

btr_search_drop_page_hash_index((buf_block_t*) bpage, NULL);

>>>計算b->zip.data的checksum,并寫入壓縮頁。

>>>將空出來的bpage加入到buf_pool->free上

? ? ?buf_LRU_block_free_hashed_page((buf_block_t*) bpage, FALSE);

C.如果buf_pool->unzip_LRU上找不到空閑塊,這時候會去從buf_pool->LRU上獲取,這種情況下,如果驅(qū)逐的是壓縮表的block,還會釋放壓縮頁 ? ? if (!freed) { ? ? ? ? freed = buf_LRU_free_from_common_LRU_list( ? ? ? ? ? ? buf_pool, n_iterations, have_LRU_mutex); ? ? }? 該函數(shù)會從buf_pool->LRU的尾部開始掃描,在沒有壓縮表的情況下這也是普遍調(diào)用的函數(shù) 函數(shù)freed = buf_LRU_free_block(bpage, TRUE, have_LRU_mutex)會被調(diào)用到去從LRU上釋放該塊,注意這里第二個參數(shù)為TRUE,這表明會同時釋放壓縮頁,并做碎片整理工作。 buf_LRU_free_block->buf_LRU_block_remove_hashed_page->buf_buddy_free->buf_buddy_free_low,另外

buf_LRU_block_remove_hashed_page中還會釋放bpage的page描述符(buf_page_free_descriptor)

related bug:http://bugs.mysql.com/bug.php?id=64344

如bug#64344所提到的,malloc/free是在持有buffer pool鎖的情況下進(jìn)行的,這會對并發(fā)操作產(chǎn)生開銷。 D.更新buf_pool->LRU_flush_ended計數(shù)并釋放buf_pool->LRU_list_mutex ? ? if (!freed) { ? ? ? ? buf_pool->LRU_flush_ended = 0; ? ? } else if (buf_pool->LRU_flush_ended > 0) { ? ? ? ? buf_pool->LRU_flush_ended–; ? ? } 4.從步驟3成功釋放了一個空閑block,則goto loop
5.n_iterations > 30時,開始打印警告和監(jiān)控信息到err log中
6.如果走到這一步,表明找不到空閑塊,嘗試刷LRU, 并喚醒AIO線程 buf_flush_free_margin(buf_pool, TRUE);
7.如果buf_pool->LRU_flush_ended > 0,表明我們已經(jīng)在一次LRU Flush中寫入了page,為了讓insert? buffer更高效,將這些page轉(zhuǎn)移到free list上(翻譯自注釋) buf_LRU_try_free_flushed_blocks(buf_pool);
8.n_iterations > 10時os_thread_sleep(500000)
9.n_iterations++; goto loop

總結(jié)

以上是生活随笔為你收集整理的[MySQL 源码] 从buffer pool中获取空闲block流程的全部內(nèi)容,希望文章能夠幫你解決所遇到的問題。

如果覺得生活随笔網(wǎng)站內(nèi)容還不錯,歡迎將生活随笔推薦給好友。