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.mzph.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…