天天看点

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

本文主要是记录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,并取其中的最大值。

继续阅读