MySQL:Innodb page clean 線程 (二) :解析

一、數據結構和入口函數

1、數據結構

?●??page_cleaner_t:整個Innodb只有一個,包含整個page clean線程相關信息。其中包含了一個page_cleaner_slot_t的指針。
變量名含義
mutex用于保護整個page_cleaner_t結構體和page_cleaner_slot_t結構體,當需要修改結構體信息的時候需要獲取這個mutex,如在pc_request函數中
is_requested一個條件變量,用于喚醒堵塞在這個條件之上的工作線程
is_finished一個條件變量,用于通知協調線程刷新工作已經完成
n_workers當前存在的工作線程總數
requested布爾值,當前是否需要進行臟數據刷新工作
lsn_limit需要刷新到lsn的位置,當需要同步刷新的時候,這個值將被賦予,以保證小于這個lsn的日志都已經完成了刷盤工作
n_slots槽的數量,槽的數量和buffer instance的數量相同
n_slots_requested當前處于需要刷新狀態下(PAGE_CLEANER_STATE_REQUESTED)的槽的數量
n_slots_flushing當前處于刷新狀態下(PAGE_CLEANER_STATE_FLUSHING)的槽的數量
n_slots_finished當前處于已經刷新完成狀態下(PAGE_CLEANER_STATE_FINISHED)的槽的數量
flush_time整個(以innodb buffer為單位)刷新消耗的時間(累計 page_cleaner->flush_time += ut_time_ms() - tm;)
flush_pass整個(以innodb buffer為單位)刷新的次數(累計 page_cleaner->flush_pass++;)
slots指針指向實際的槽
is_running布爾值,如果關閉innodb會被設置為false,進行強行刷新臟數據
?●??page_cleaner_slot_t:每個buffer instance都包含一個這樣的結構體,page clean工作線程刷新的時候每個線程都會輪詢的檢測每個槽,直到找到沒有被其他page clean線程刷新的槽進行刷新工作或者所有的槽(buffer instance )都刷新完成為止。參考pc_flush_slot函數。
變量名含義
state狀態PAGE_CLEANER_STATE_REQUESTED、PAGE_CLEANER_STATE_FLUSHING和PAGE_CLEANER_STATE_FINISHED中的一種
n_pages_requested本槽需要刷新的總的塊數量
n_flushed_list已經刷新的塊數
succeeded_list布爾值,刷新是否完成
flush_list_time本槽刷新消耗的時間(累計參考pc_flush_slot函數)
flush_list_pass本槽進行刷新操作的次數(累計參考pc_flush_slot函數)

2、入口函數

?●??協調工作線程入口:buf_flush_page_cleaner_coordinator

?●??工作線程入口:buf_flush_page_cleaner_worker

二、主循環解析

其由函數buf_flush_page_cleaner_coordinator實現。實際正常運行情況下的工作都包含在while (srv_shutdown_state == SRV_SHUTDOWN_NONE) 這個大循環下。

1、是否需要睡眠1秒判斷

首先如果沒有活躍的change buffer 并且沒有pending的物理塊,并且上次刷新的塊數量不為0,則不需要睡眠1秒,源碼總有如下解釋:

/* The page_cleaner skips sleep if the server is idle and there are no pending IOs in the buffer pool and there is work to do. */

 
if (srv_check_activity(last_activity)
|| buf_get_n_pending_read_ios()
ret_sleep = pc_sleep_if_needed(
|| n_flushed == 0){
if (srv_shutdown_state != SRV_SHUTDOWN_NONE) {
next_loop_time, sig_count); //睡眠一秒 break; }
} else if (ut_time_ms() > next_loop_time) { //如果當前時間大于 上次刷新 時間+1 秒則 設置為OS_SYNC_TIME_EXCEEDED
ret_sleep = OS_SYNC_TIME_EXCEEDED; } else { ret_sleep = 0;
}

但是這個睡眠是可以被喚醒的,比如同步刷新應該就會喚醒它(buf_flush_request_force函數)。參考函數os_event::wait_time_low

2、IO能力不足警告

如前文所描述這里產生如下警告:

page_cleaner: 1000ms intended loop took **ms. The settings might not be optimal.((flushed="**" , during the time.)

源碼片段:

 
if (curr_time > next_loop_time + 3000) { //如果刷新時間 大于了 上次時間 +1 秒+3 秒 則報info
if (warn_count == 0) { ib::info() << "page_cleaner: 1000ms"
<< "ms. The settings might not"
" intended loop took " << 1000 + curr_time - next_loop_time
if (warn_interval > 300) {
" be optimal. (flushed=" << n_flushed_last << ", during the time.)";
}
warn_interval = 600; } else {
warn_interval *= 2;
3、同步刷新判斷
?●??觸發條件
 
(ret_sleep != OS_SYNC_TIME_EXCEEDED
&& srv_flush_sync
&& buf_flush_sync_lsn > 0)

同步會喚醒正在睡眠狀態的page clean協調工作線程那么睡眠應該不會滿足一秒的條件所以不會被標記為OS_SYNC_TIME_EXCEEDED,同時srv_flush_sync和buf_flush_sync_lsn均會被設置接下來就是喚醒工作線程進行刷新,同時本協調線程也完成部分任務。

?●??工作代碼
 
pc_request(ULINT_MAX, lsn_limit); //喚醒page clean 工作線程干活
/* Coordinator also treats requests */ //協調者同樣要完成部分任務
while (pc_flush_slot() > 0) {}
?●??喚醒操作

如前文描述在checkpoint或者DML語句執行過程中都會通過log_free_check檢查是否redo log處于安全的狀態,如果不安全就會調用如下代碼(log_preflush_pool_modified_pages函數中)喚醒page clean線程進行同步刷新:

 
if (srv_flush_sync) {
/* wake page cleaner for IO burst */
buf_flush_request_force(new_oldest); //設置全局變量同時通過broadcast喚醒同步刷新
}
buf_flush_wait_flushed(new_oldest); //所有線程等待同步刷新完成

4、活躍刷新

?●??觸發條件
srv_check_activity(last_activity)

這里判斷是否有活躍的線程,所謂活躍就是調用srv_inc_activity_count函數進行增加的,一般來講DML和DDL會標記為活躍,purge線程及其工作線程工作期間會標記為活躍。可以將斷點做到srv_inc_activity_count進行debug。所以線上數據庫DML比較多所以一般都會是活躍刷新。

?●??工作代碼

這里涉及到刷新多少個塊計算主要函數為 page_cleaner_flush_pages_recommendation,后面在討論。

 
n_to_flush = page_cleaner_flush_pages_recommendation(&lsn_limit, last_pages);
//此處n_to_flush就是本次需要刷新的塊數的數量
/* Coordinator also treats requests */ //工作協調線程同樣要完成部分任務
pc_request(n_to_flush, lsn_limit); //喚醒page clean 工作線程干活
pc_wait_finished(&n_flushed_list);//等待其他刷新完成
while (pc_flush_slot() > 0) {}

5、空閑刷新

?●??觸發條件
else if (ret_sleep == OS_SYNC_TIME_EXCEEDED)

當睡足了1秒,并且沒有活躍的線程。那么就進行空閑刷新,一般來講如果沒有DML/DDL等語句那么應該進行是空閑刷新。

?●??工作代碼
 
buf_flush_lists(PCT_IO(100), LSN_MAX, &n_flushed); //io能力 刷新到那個lsn 以及傳出刷新的塊數量
//PCT_IO是一個宏如下:
#define PCT_IO(p) ((ulong) (srv_io_capacity * ((double) (p) / 100.0)))

可以看到這里的百分比直接是100%及按照innodb_io_capacity參數的設定進行刷新。

當然這里只是看了正常期間工作的代碼,如果是Innodb shutdown也會觸發同步刷新。可自行參考代碼。

三、page_cleaner_flush_pages_recommendation函數

前面提過這個函數,是活躍刷新刷新塊的計算函數,下面直接給出整個代碼

 
{
cur_lsn = log_get_lsn();//獲取當前的lsn 在 redo buffer中的
if (prev_lsn == 0) { //靜態變量如果是0則代表是第一次執行本函數
prev_time = ut_time(); //獲取當前時間
/* First time around. */ prev_lsn = cur_lsn; return(0); }
sum_pages += last_pages_in;
if (prev_lsn == cur_lsn) { //如果沒有redo日志生成 return(0); } time_t curr_time = ut_time();
((static_cast<double>(sum_pages)
double time_elapsed = difftime(curr_time, prev_time); avg_page_rate = static_cast<ulint>( / time_elapsed)
/* How much LSN we have generated since last call. */
+ avg_page_rate) / 2); //算出上次刷新每秒刷新的pages數量,同時加上次計算的每秒平均刷新塊數 然后除以2 得到一個每秒刷新的pages數量 !!!第一個計算條件avg_page_rate 生成 lsn_rate = static_cast<lsn_t>( static_cast<double>(cur_lsn - prev_lsn) / time_elapsed);//計算redo lsn生成率
ulint flush_pass = page_cleaner->flush_pass;
lsn_avg_rate = (lsn_avg_rate + lsn_rate) / 2;//計算redo每秒平均生成率 /* aggregate stats of all slots */ mutex_enter(&page_cleaner->mutex); ulint flush_tm = page_cleaner->flush_time; page_cleaner->flush_time = 0; page_cleaner->flush_pass = 0; ulint list_tm = 0;
mutex_exit(&page_cleaner->mutex);
ulint list_pass = 0; for (ulint i = 0; i < page_cleaner->n_slots; i++) {//掃描所有的槽 page_cleaner_slot_t* slot; slot = &page_cleaner->slots[i]; list_tm += slot->flush_list_time; list_pass += slot->flush_list_pass; slot->flush_list_time = 0; slot->flush_list_pass = 0; }
pct_for_lsn = af_get_pct_for_lsn(age);//計算出lsn的比率 百分比(l列如4.5)
oldest_lsn = buf_pool_get_oldest_modification(); //獲取flush list中最老的ls ut_ad(oldest_lsn <= log_get_lsn());//斷言 age = cur_lsn > oldest_lsn ? cur_lsn - oldest_lsn : 0; //獲取當前LSN和最老LSN的之間的差值 pct_for_dirty = af_get_pct_for_dirty(); //計算出一個刷新百分比 (比如100) !!!!重點 pct_total = ut_max(pct_for_dirty, pct_for_lsn);//取他們的大值
buf_pool_t* buf_pool = buf_pool_from_array(i);
/* Estimate pages to be flushed for the lsn progress *///計算target_lsn ulint sum_pages_for_lsn = 0; lsn_t target_lsn = oldest_lsn + lsn_avg_rate * buf_flush_lsn_scan_factor; //計算下一次刷新的 目標lsn 及target_lsnbuf_flush_lsn_scan_factor是定值3 for (ulint i = 0; i < srv_buf_pool_instances; i++) {//循環整個buffer instance找到小于target_lsn的臟塊 ulint pages_for_lsn = 0;
sum_pages_for_lsn += pages_for_lsn; //這里匯總所有 innodb buffer實例中 flush list 小于這個 target lsn 的 page 總數
buf_flush_list_mutex_enter(buf_pool); for (buf_page_t* b = UT_LIST_GET_LAST(buf_pool->flush_list);//每個innodb buffer的末尾的flush list 進行掃描,頭插法? b != NULL; b = UT_LIST_GET_PREV(list, b)) { if (b->oldest_modification > target_lsn) { break; } ++pages_for_lsn; //某個 innodb buffer 實例中 flush list 小于這個 target lsn 的 page計數 } buf_flush_list_mutex_exit(buf_pool);
/* Cap the maximum IO capacity that we are going to use by
mutex_enter(&page_cleaner->mutex); ut_ad(page_cleaner->slots[i].state == PAGE_CLEANER_STATE_NONE);//斷言所有的槽處于沒有刷新狀態 page_cleaner->slots[i].n_pages_requested = pages_for_lsn / buf_flush_lsn_scan_factor + 1; //確認槽的n_pages_requested值 mutex_exit(&page_cleaner->mutex); } sum_pages_for_lsn /= buf_flush_lsn_scan_factor;//buf_flush_lsn_scan_factor為定值3
n_pages = (n_pages + avg_page_rate + pages_for_lsn) / 3; // 3部分組成 1、根據參數計算出來的IO能力 2、以往每秒刷新頁的數量 3、根據target lsn 計算出來的一個需要刷新的塊數
max_io_capacity. Limit the value to avoid too quick increase */ n_pages = PCT_IO(pct_total); //根據 前面得到的 pct_total 和 srv_io_capacity參數得到 刷新的塊數 !!!第二個計算參數生成。 if (age < log_get_max_modified_age_async()) { //如果日質量小于 異步刷新的范疇 ulint pages_for_lsn = std::min<ulint>(sum_pages_for_lsn, srv_max_io_capacity * 2); //即便是需要刷新的塊數很多,最多只能刷max_io_capacity*2的數量!!!第三個計算參數生成 } if (n_pages > srv_max_io_capacity) {
}
n_pages = srv_max_io_capacity; }
return(n_pages);

此函數最后計算出了需要刷新的塊,其中刷新比率計算的的重點函數為af_get_pct_for_dirty和af_get_pct_for_lsn 下面將給出代碼注釋,其實前文中的算法就來自af_get_pct_for_dirty。

四、af_get_pct_for_dirty和af_get_pct_for_lsn函數

?●??af_get_pct_for_dirty函數
 
double dirty_pct = buf_get_modified_ratio_pct(); //得到 修改的塊/總的塊的 的百分比 記住臟數據比率
if (dirty_pct == 0.0) { /* No pages modified */ return(0); }
if (srv_max_dirty_pages_pct_lwm == 0) { //如果innodb_max_dirty_pages_pct_lwm沒有設置
ut_a(srv_max_dirty_pages_pct_lwm <= srv_max_buf_pool_modified_pct);
if (dirty_pct >= srv_max_buf_pool_modified_pct) { //如果臟數據比率大于了innodb_max_dirty_pages_pct則返回比率100%
/* The user has not set the option to preflush dirty pages as we approach the high water mark. */
return(100);
/* We have crossed the high water mark of dirty pages In this case we start flushing at 100% of innodb_io_capacity. */ }
/* We should start flushing pages gradually. */ //innodb_max_dirty_pages_pct_lwm參數設置
} else if (dirty_pct >= srv_max_dirty_pages_pct_lwm) { //如果設置了innodb_max_dirty_pages_pct_lwm 并且臟數據比率大于了 return(static_cast<ulint>((dirty_pct * 100)
return(0);//否則返回0
/ (srv_max_buf_pool_modified_pct + 1))); //則返回 (臟數據比率/(innodb_max_dirty_pages_pct+1))*100 也是一個比率 如(45/76)*100 }

?●??af_get_pct_for_lsn函數:

注意innodb_cleaner_lsn_age_factor參數默認設置為high_checkpoint,可以看到算法最后是除以700.5,所有前文我說這個函數算出來的比率一般比較小。

 
if (age < max_async_age && !srv_adaptive_flushing) { //如果小于異步刷新 且 自適應flush 沒有開啟
/* We have still not reached the max_async point and
/* If we are here then we know that either:
the user has disabled adaptive flushing. */ return(0); }
2) User may have disabled adaptive flushing but we have reached
1) User has enabled adaptive flushing max_async_age. */
lsn_age_factor = (age * 100) / max_async_age; //比率lsn_age_factor = (本次刷新的日志量/(logtotalsize*(9/10)*(7/8)))
ut_ad(srv_max_io_capacity >= srv_io_capacity); switch ((srv_cleaner_lsn_age_factor_t)srv_cleaner_lsn_age_factor) { case SRV_CLEANER_LSN_AGE_FACTOR_LEGACY:
case SRV_CLEANER_LSN_AGE_FACTOR_HIGH_CHECKPOINT: //innodb_cleaner_lsn_age_factor參數默認設置為high_checkpoint
return(static_cast<ulint>( ((srv_max_io_capacity / srv_io_capacity) * (lsn_age_factor * sqrt((double)lsn_age_factor))) / 7.5)); //430 return(static_cast<ulint>(
* (lsn_age_factor * lsn_age_factor //(10 * (3.3*10*10))/700 =4.3
((srv_max_io_capacity / srv_io_capacity) // ((max_io_cap /io_cap) * (sqrt(lsn_age_factor)*lsn_age_factor*lsn_age_factor))/700.5 * sqrt((double)lsn_age_factor)))
/ 700.5)); //

五、總結

本文只是解釋了對于page clean線程的結構和刷新方式,但是真正的刷新工作實際上從pc_flush_slot函數調用才開始,后面非常復雜。


原文發布時間為:2018-11-14

本文作者:重慶八怪

本文來自云棲社區合作伙伴“老葉茶館”,了解相關信息可以關注“老葉茶館”。

本文來自互聯網用戶投稿,該文觀點僅代表作者本人,不代表本站立場。本站僅提供信息存儲空間服務,不擁有所有權,不承擔相關法律責任。
如若轉載,請注明出處:http://www.pswp.cn/news/389240.shtml
繁體地址,請注明出處:http://hk.pswp.cn/news/389240.shtml
英文地址,請注明出處:http://en.pswp.cn/news/389240.shtml

如若內容造成侵權/違法違規/事實不符,請聯系多彩編程網進行投訴反饋email:809451989@qq.com,一經查實,立即刪除!

相關文章

Lockdown Wheelie項目

“It’s Strava for wheelies,” my lockdown project, combining hyper-local exercise with data analytics to track and guide improvement. Practising wheelies is a great way to stay positive; after all, it’s looking up, moving forward.我的鎖定項目“將Strava運…

api地理編碼_通過地理編碼API使您的數據更有意義

api地理編碼Motivation動機 In my second semester of my Master’s degree, I was working on a dataset which had all the records of the road accident in Victoria, Australia (2013-19). I was very curious to know, which national highways are the most dangerous …

js進階 12-5 jquery中表單事件如何使用

js進階 12-5 jquery中表單事件如何使用 一、總結 一句話總結&#xff1a;表單事件如何使用&#xff1a;可元素添加事件監聽&#xff0c;然后監聽元素&#xff0c;和javase里面一樣。 1、表單獲取焦點和失去焦點事件有哪兩組&#xff1f; 注意是blur/focus和focus in/out&#x…

SiamBAN論文學習

SiameseBAN論文來源論文背景主要貢獻論文分析網絡框架創新點一&#xff1a;Box Adaptive Head創新點二&#xff1a;Ground-truth創新點三&#xff1a;Anchor Free論文流程訓練部分&#xff1a;跟蹤部分論文翻譯Abstract1. Introduction2. Related Works2.1. Siamese Network Ba…

簡單入門Javascript正則表達式

我們已經會熟練使用js字符串類型了&#xff0c;例如你想知道一個變量是否等于一個字符串&#xff0c;可能可能這樣判斷 if(ahello,world){... } 復制代碼但是往往我們有時候對一些字符串判斷顯得力不從心&#xff0c;例如判斷一個文件的類型是否為js類型&#xff0c;可能有下面…

實現klib_使用klib加速數據清理和預處理

實現klibTL;DRThe klib package provides a number of very easily applicable functions with sensible default values that can be used on virtually any DataFrame to assess data quality, gain insight, perform cleaning operations and visualizations which results …

MMDetection修改代碼無效

最近在打比賽&#xff0c;使用MMDetection框架&#xff0c;但是無論是Yolo修改類別還是更改head&#xff0c;代碼運行后發現運行的是修改之前的代碼。。。也就是說修改代碼無效。。。 問題解決辦法&#xff1a; MMDetection在首次運行后會把一部分運行核心放在anaconda的環境…

docker etcd

etcd是CoreOS團隊于2013年6月發起的開源項目&#xff0c;它的目標是構建一個高可用的分布式鍵值(key-value)數據庫&#xff0c;用于配置共享和服務發現 etcd內部采用raft協議作為一致性算法&#xff0c;etcd基于Go語言實現。 etcd作為服務發現系統&#xff0c;有以下的特點&…

SpringBoot簡要

2019獨角獸企業重金招聘Python工程師標準>>> 簡化Spring應用開發的一個框架&#xff1b;      整個Spring技術棧的一個大整合&#xff1b;      J2EE開發的一站式解決方案&#xff1b;      自動配置&#xff1a;針對很多Spring應用程序常見的應用功能&…

發送郵件 的類 C# .net

/// <summary> /// 發送郵件 /// </summary> /// <param name"SendTo">發送人的地址</param> /// <param name"MyEmail">我的Email地址</param> /// <param name"SendTit…

簡明易懂的c#入門指南_統計假設檢驗的簡明指南

簡明易懂的c#入門指南介紹 (Introduction) One of the main applications of frequentist statistics is the comparison of sample means and variances between one or more groups, known as statistical hypothesis testing. A statistic is a summarized/compressed proba…

計算機科學期刊_成為數據科學家的五種科學期刊

計算機科學期刊The field of data science is advancing at an incredible pace. New scientific articles are published daily. As a student, I try to stay up-to-date with the scientific literature that is published. In this blog post, I created a list of scienti…

Torch.distributed.elastic 關于 pytorch 不穩定

錯誤日志&#xff1a; Epoch: [229] Total time: 0:17:21 Test: [ 0/49] eta: 0:05:00 loss: 1.7994 (1.7994) acc1: 78.0822 (78.0822) acc5: 95.2055 (95.2055) time: 6.1368 data: 5.9411 max mem: 10624 WARNING:torch.distributed.elastic.agent.server.api:Rec…

0x22 迭代加深

poj2248 真是個新套路。還有套路剪枝...大到小和判重 #include<cstdio> #include<iostream> #include<cstring> #include<cstdlib> #include<algorithm> #include<cmath> #include<bitset> using namespace std;int n,D,x[110];bool…

云原生全球最大峰會之一KubeCon首登中國 Kubernetes將如何再演進?

雷鋒網消息&#xff0c;11月14日&#xff0c;由CNCF發起的云原生領域全球最大的峰會之一KubeConCloudNativeCon首次登陸中國&#xff0c;中國已經成為云原生領域一股強大力量&#xff0c;并且還在不斷成長。 毫無疑問&#xff0c;Kubernetes已經成為容器編排事實標準&#xff…

分布分析和分組分析_如何通過群組分析對用戶進行分組并獲得可行的見解

分布分析和分組分析數據分析 (DATA ANALYSIS) Being a regular at a restaurant is great.乙 eing定期在餐廳是偉大的。 When I started university, my dad told me I should find a restaurant I really liked and eat there every month with some friends. Becoming a reg…

python 工具箱_Python交易工具箱:通過指標子圖增強圖表

python 工具箱交易工具箱 (trading-toolbox) After a several months-long hiatus, I can finally resume posting to the Trading Toolbox Series. We started this series by learning how to plot indicators (specifically: moving averages) on the top of a price chart.…

PDA端的數據庫一般采用的是sqlce數據庫

PDA端的數據庫一般采用的是sqlce數據庫,這樣與PC端的sql2000中的數據同步就變成了一個問題,如在PDA端處理,PDA端的內存,CPU等都是一個制約因素,其次他們的一個連接穩定及其間的數據傳輸也是一個難點.本例中通過在PC端的轉化后再復制到PDA上面,這樣,上面所有的問題都得到了一個有…

bzoj 1016 [JSOI2008]最小生成樹計數——matrix tree(相同權值的邊為階段縮點)(碼力)...

題目&#xff1a;https://www.lydsy.com/JudgeOnline/problem.php?id1016 就是縮點&#xff0c;每次相同權值的邊構成的聯通塊求一下matrix tree。注意gauss里的編號應該是從1到...的連續的。 學習了一個TJ。用了vector。自己曾寫過一個只能過樣例的。都放上來吧。 路徑壓縮的…

區塊鏈的模型結構

關于區塊鏈的模型結構問題&#xff0c;行業內已經談論千萬遍了&#xff0c;基本上已經成為一種定義式的問題了。總體上來看&#xff0c;區塊鏈的基礎架構可以分為六層&#xff0c;包括數據層、網絡層、共識層、激勵層、合約層、應用層。每一層分別完成一項核心的功能&#xff0…