Innodb:如何计算异步/同步刷脏及checkpoint的临界范围

本文涉及的产品
日志服务 SLS,月写入数据量 50GB 1个月
简介:
本文主要是记录Innodb在初始化日志子系统时,如何计算异步/同步刷脏或checkpoint的临界范围
代码分析基于MySQL 5.6.11
///////////////////////////////////////////////////////////////////////////////////////////////

相关配置为:
innodb_log_buffer_size=200M
innodb_log_file_size=1000M
innodb_log_files_in_group=4
innodb_flush_log_at_trx_commit = 1
innodb_thread_concurrency = 64
 
刷脏和做checkpoint的临界条件在系统启动初始化日志系统时即被确定了,backtrace 如下:
innobase_start_or_create_for_mysql
     ->log_group_init
2151                 log_group_init(0, i, srv_log_file_size * UNIV_PAGE_SIZE,

2152                                SRV_LOG_SPACE_FIRST_ID,

2153                                SRV_LOG_SPACE_FIRST_ID + 1);

 
                 ->log_calc_max_ages
                    
 
log_calc_max_ages:
 
1.计算当前redo log日志组的最大容纳LSN(log_group_get_capacity):
smallest_capacity =  (group->file_size – LOG_FILE_HDR_SIZE) * group->n_files = (innodb_log_file_size-4*512)*4 = 4194295808
 
smallest_capacity =  smallest_capacity – smallest_capacity / 10;  // 为了安全起见,保留10分之一的空间
 
2.检查在当前thread concurrency(srv_thread_concurrency)限制下,日志组是否拥有足够的空闲空间:
        free = LOG_CHECKPOINT_FREE_PER_THREAD * (10 + srv_thread_concurrency)+ LOG_CHECKPOINT_EXTRA_FREE  =  (4*16384) * (10 + 64) + 8*16384 = 4980736
 
如果free > smallest_capacity /2的话,那么就会错误退出,表明innodb_log_file_size设的太小了。
否则,预留边界:
 margin = smallest_capacity – free;
 margin = margin – margin / 10  = 3392896943;
 
 
实际上margin的计算可以总结为:
margin = {
                            [ (innodb_log_file_size-LOG_FILE_HDR_SIZE) * innodb_log_files_in_group ] * (9/10) 
                            – 
                            [ LOG_CHECKPOINT_FREE_PER_THREAD * (10 + innodb_thread_concurrency) + LOG_CHECKPOINT_EXTRA_FREE]
               } * (9/10)
 
 
 
 
log_sys->log_group_capacity = smallest_capacity  =   3774866228;              
 
log_sys->max_modified_age_async = margin– margin / LOG_POOL_PREFLUSH_RATIO_ASYNC  =  margin * (7/8)  = 2968784826           //当lsn -buf_pool_get_oldest_modification()超过这个值时, 需要做异步刷脏页

 
log_sys->max_modified_age_sync = margin– margin / LOG_POOL_PREFLUSH_RATIO_SYNC    = margin *(15/16)    = 3180840885          //当lsn -buf_pool_get_oldest_modification()超过这个值时, 需要做同步刷脏页

 
log_sys->max_checkpoint_age_async = margin – margin

                / LOG_POOL_CHECKPOINT_RATIO_ASYNC           =   margin *(31/32) = 3286868914          //当lsn – last_checkpoint_lsn超过该值时,需要做一次异步checkpoint

 

log_sys->max_checkpoint_age = margin =  3392896943                                                                   //lsn – last_checkpoint_lsn不允许超过该值,否则需要做同步checkpoint

 
 
上面这几个变量,除了第一个之外,都是用于在函数log_checkpoint_margin中进行判断,log_sys->check_flush_or_checkpoint是判断是否flush/checkpoint的关键变量
 
主要判断逻辑如下:
 
1.如果log_sys->check_flush_or_checkpoint为false,直接返回,表明不需要flush或者checkpoint
 
2.找出当前所有buffer pool实例中最老的oldesrt_lsn,从每个bp->flush_list的尾部读取(log_buf_pool_get_oldest_modification())
 
3.如果log->lsn – oldest_lsn大于log_sys->max_modified_age_sync,表明需要做一次同步刷新,将lsn 推进到新的lsn位置(log_preflush_pool_modified_pages):
 LSN范围为:2 * (log->lsn – oldest_lsn – log_sys->max_modified_age_sync)  + oldest_lsn
 
             如果刷新失败,表明同时有别的线程也在刷脏,则将check_flush_or_checkpoint设置为TRUE,回到第一步
 
4.判断是否需要做checkpoint
checkpoint_age = log->lsn – log->last_checkpoint_lsn;
如果checkpoint_age > log_sys->max_checkpoint_age, 表示需要做一次同步checkpoint;
如果 log_sys->max_checkpoint_age_async  < checkpoint_age   <=  log_sys->max_checkpoint_age ,表示需要做一次异步的checkpoint,并设置log_sys->check_flush_or_checkpoint = false;
如果checkpoint_age  <=log_sys->max_checkpoint_age_async  ,则无需做checkpoint,并设置log_sys->check_flush_or_checkpoint = false;
 
checkpoint 调用函数log_checkpoint(checkpoint_sync, FALSE); 如果是同步checkpoint(checkpoint_sync为TRUE) 还要返回到第1步继续判断
 
另外,这几个变量在函数log_close中会被用到,它会去做一件重要的事情:设置log_sys->check_flush_or_checkpoint。
 
至于异步刷脏,log_sys->max_modified_age_async被封装在函数log_get_max_modified_age_async中, 被函数af_get_pct_for_lsn。显而易见,异步刷脏是由page cleaner线程来完成的。
在函数af_get_pct_for_lsn中,根据当前的LSN,计算需要以IO capacity的百分之几来刷脏
 
当当前lsn-buf_pool_get_oldest_modification()超过log_sys->max_modified_age_async时:
 
age = log_sys->lsn – buf_pool_get_oldest_modification()
lsn_age_factor = (age *100)/log_sys->max_modified_age_async )
 
返回值为:
pct =   [
                 (innodb_io_capacity_max/innodb_io_capacity) 
                 *
                 (lsn_age_factor * sqrt((double)lsn_age_factor))
          ] /7.5
 
 
另外,如果参数innodb_adaptive_flushing设置为OFF,且没有超过log_get_max_modified_age_async()的话,直接返回0
 
Page cleaner线程会同时根据LSN 和脏页比例来获取pct,并取其中的最大值。

相关实践学习
日志服务之使用Nginx模式采集日志
本文介绍如何通过日志服务控制台创建Nginx模式的Logtail配置快速采集Nginx日志并进行多维度分析。
相关文章
|
8月前
|
存储 SQL 关系型数据库
MySQL之深入InnoDB存储引擎——Checkpoint机制
一、引入 由于页的操作首先都是在缓冲池中完成的,那么如果一条DML语句改变了页中的记录,那么此时页就是脏的,即缓冲池中页的版本要比磁盘的新。那么数据库需要将新版本的页刷新到磁盘。倘若每次一个页发生变化就刷新,那么开销会很大,若热点数据集中在某几个页中,那么数据库的性能将变得非常差。 同时如果在缓冲池将新版本的页刷新到磁盘时发生了宕机,那么数据就不能恢复了。为了避免发生数据丢失的问题,当前事务数据库普遍都采用了 Write Ahead Log 策略,即当事务提交时,先写重做日志,再修改页。当由于发生宕机而导致数据丢失时,通过重做日志来完成数据的恢复,从而满足事务的持久性要求。
|
关系型数据库 MySQL
MySQL:Innodb表 Data free 的计算概要
简单记录一下,因为看了一下Data free的计算还算准确。不是统计值大概是空闲extent的大小。 ST_FIELD_INFO tables_fields_info[]= {... {"DATA_FREE", MY_INT64_NUM_DECIMAL_DIGITS, MYSQL_TYPE_L...
4548 0
|
关系型数据库 MySQL 数据库
MySQL · 源码分析 · Innodb缓冲池刷脏的多线程实现
简介 为了提高性能,大多数的数据库在操作数据时都不会直接读写磁盘,而是中间经过缓冲池,将要写入磁盘的数据先写入到缓冲池里,然后在某个时刻后台线程把修改的数据刷写到磁盘上。MySQL的InnoDB引擎也使用缓冲池来缓存从磁盘读取或修改的数据页,如果当前数据库需要操作的数据集比缓冲池中的空闲页面大的话,当前缓冲池中的数据页就必须进行脏页淘汰,以便腾出足够的空闲页面供当前的查询使用。
1505 0
|
关系型数据库 MySQL 索引
如何计算指定的InnoDB索引大小
前言 通常情况下,获取InnoDB索引的大小通常的方法是show table status,但是如果想获取指定的索引大小呢? 通常情况下我们想看索引大小的话,用的是 show table status like ""\G 例1: mysql> show create table sbtest1\G *************************** 1.
4473 0
|
存储 监控 JavaScript
《MySQL技术内幕:InnoDB存储引擎第2版》——2.4 Checkpoint技术
本节书摘来自华章计算机《MySQL技术内幕:InnoDB存储引擎第2版》一书中的第2章,第2.4节,作者:姜承尧著, 更多章节内容可以访问云栖社区“华章计算机”公众号查看。
1148 0
|
存储 关系型数据库 MySQL
MYSQL INNODB 如何计算B+树表的最大容量和行数
考虑表结构如下: create table testzh(id int  primary key auto_increment ,id2 int,id3 int); 插入数据: delimiter //  create procedure ins3()   ...
2106 0