天天看点

[MySQL 5.6] Innodb后台线程之master线程

在mysql 5.6中,master线程的工作已经被大大减轻,类似purge, page clean都分配给独立的后台线程来进行。那么现在master线程还需要干啥活儿呢。以下就是本文需要介绍的部分

简单的看看代码,函数入口不变,依旧是srv_master_thread,但相对5.5的代码,这里已经非常非常精简了。

概括的说,master线程干这么几件事儿:

a. 每sleep 1秒钟

检查最近1秒内是否有活跃事件,这是一个全局计数器,在几个地方会被递增(通过函数srv_inc_activity_count):

1.srv_active_wake_master_thread—> 实际上在5.6里这里只剩下计数器的功能了,因为除非是以force recovery 启动,或者被shutdown了,否则不会进入suspend状态

3.row_undo_step->事务回滚时

例如事务提交/prepare时(innobase_commit->srv_active_wake_master_thread) ,事务回滚时,一个简单的查询都会引起计数器增加,从而让master线程判定现在系统正忙.

a.1如果认定现在系统正忙,则调用函数srv_master_do_active_tasks,做如下工作:

1.log_free_check

总会检查redo中是否有足够的空间,以确定是否做flush或者做checkpoint,通常情况下,用户线程在写redo日志之前也会无条件调用该函数。 这里会先在无加锁的情况下,检查log_sys->check_flush_or_checkpoint是否为true,如果为true,则调用log_check_margins(),否则直接返回。 check_flush_or_checkpoint在函数log_close中被设置,backtrace如下:       mtr_commit->mtr_log_reserve_and_write->log_close() 在每次将一个mtr日志写到buffer后,总会调用log_close()函数,注意,该函数是持有log_sys->mutex锁的 有以下几种情况会去设置check_flush_or_checkpoint为true:

log_sys->buf_free > log_sys->max_buf_free

log_sys->lsn-buf_pool_get_oldest_modification() >log_sys->max_modified_age_sync

log_sys->lsn-log_sys->last_checkpoint_lsn > log_sys->max_checkpoint_age_async

todo: mtr的组织,如何提交,以及redo 日志在内存中的控制 log_free_check会调用log_check_margins做两件事: 1)调用log_flush_margin:首先确认log->buf_free 是否大于 log->max_buf_free,如果是,则需要将日志写到文件,到当前lsn(log_write_up_to(lsn, log_no_wait, false))。如果已经有别的线程在干这活儿,则啥也不干,返回 2)调用log_checkpoint_margin,判断是否达到redo的同步刷脏点,或者异步/同步checkpoint点,决定是否刷脏(log_preflush_pool_modified_pages)及做checkpoint(log_checkpoint) 3)如果log_sys->check_flush_or_checkpoint依然为true,则回到1)继续。

2.ibuf_contract_in_background(0, false);

做ibuf merge, 正常情况下,每次处理innodb_io_capacity*0.05个page, 但如果ibuf->size > ibuf->max_size / 2,则处理: n_pages = innodb_io_capacity   *   { [((ibuf->size – ibuf->max_size / 2)*100)/(ibuf->max_size + 1)]/100 } 这种情况说明change buffer太多了,需要合并更多的page

3.srv_sync_log_buffer_in_background();

如果需要的话,sync日志到磁盘 master线程通过该函数确保每隔一段时间,刷一次redo日志到磁盘log_buffer_sync_in_background(true)->log_write_up_to(log_sys->lsn, log_no_wait, true) 时间间隔由innodb_flush_log_at_timeout来控制,单位为秒。 todo:需要检查,如果innodb_flush_log_at_trx_commit设为1时,是否还需要调用srv_sync_log_buffer_in_background()函数

4.如果打开了mem_periodic_check宏(默认关闭),则每隔13秒,检查是否出现内存损坏(mem_validate_all_blocks)

5.每隔47秒(srv_master_dict_lru_interval)检查一次dict cache。

srv_master_evict_from_table_cache(50) 需要持有dict_operation_lock的x锁,以及dict_sys->mutex dict_make_room_in_cache :最大允许的table cache大小由table_definition_cache来决定 > 如果当前dict_sys->table_lru的长度尚小于table_definition_cache,无需检测,直接返回 >否则,从table_lru尾部开始,对于可以驱逐的表(dict_table_remove_from_cache_low),从dict cache中移除(dict_table_remove_from_cache_low),直到检测长度超过pct_check(这里是50%)或者dict cache长度<=table_definition_cache停止扫描 满足如下条件的表可以被从dict cache中驱逐: 1.当前没有被任何事务引用(table->n_ref_count = 0 ) 2.该表上没有表锁或和录锁(table->locks) ==  0  &&  table->n_rec_locks == 0) 3.表上的索引没有被adaptive hash index引用(index->search_info->ref_count 为0) 这也是5.6的一点优化,主要是防止数据词典过大导致太大的内存消耗.如果内存对你而言不是问题,那就尽量调大table_definition_cache吧。

6.每隔7秒(srv_master_checkpoint_interval)做一次新的checkpoint

log_checkpoint(true, false); 

a.2如果认定现在系统正处于空闲,则调用函数srv_master_do_idle_tasks

1.检查redo log_free_check();

2.做一次ibuf merge  

ibuf_contract_in_background(0, true);

3.检查dict cache ,srv_master_evict_from_table_cache(100);

4.刷日志(srv_sync_log_buffer_in_background)

5.做一次新的checkpoint .log_checkpoint

idle 和 active的时候,所做的事情几乎是一样的,不同的是,在active状态下,每47秒才检查dict cache,每7秒才做一次check point 

因此在idle状态下,master线程可能会更加繁忙

b.在关闭实例时

调用srv_master_do_shutdown_tasks

1.log_free_check()

2.ibuf_contract_in_background(0, true)

3.srv_sync_log_buffer_in_background()

4.log_checkpoint(true, false);

5.                srv_shutdown_print_master_pending(

                        last_print_time, n_tables_to_drop, n_bytes_merged);

继续阅读