[mysql 源码笔记] 主线程master thread源码分析

注:当前mysql版本为:mysql-5.7.41

mysql-5.7.41/storage/innobasesrv/srv0srv.cc文件下找到 os_thread_ret_t DECLARE_THREAD(srv_master_thread) 方法,就是主线程执行方法

1. 主线程declare_thread(srv_master_thread):

        1. 数据库处于恢复状态时,暂停执行主线程

        2. 数据库处于非空闲状态,执行srv_master_do_active_tasks进行数据回收

        3. 数据库处于非空闲状态,执行srv_master_do_idle_tasks方法信息数据回收

2.srv_master_thread 方法如下:

/*********************************************************************//**
The master thread controlling the server.
@return a dummy parameter */
extern "C"
os_thread_ret_t
DECLARE_THREAD(srv_master_thread)(
/*==============================*/
	void*	arg MY_ATTRIBUTE((unused)))
			/*!< in: a dummy parameter required by
			os_thread_create */
{
    // 日志打印
	DBUG_ENTER("srv_master_thread");

	srv_slot_t*	slot;
	ulint		old_activity_count = srv_get_activity_count();
	ib_time_monotonic_t	last_print_time;
loop:
    
    // 数据库启动时,检查是否需要数据库恢复操作
	if (srv_force_recovery >= SRV_FORCE_NO_BACKGROUND) {
		goto suspend_thread;
	}

    // 验证mysql当前状态, 服务暂停时结束循环
	while (srv_shutdown_state == SRV_SHUTDOWN_NONE) {

        // 进入下面流程之前 主线程休息一秒
		srv_master_sleep();

		MONITOR_INC(MONITOR_MASTER_THREAD_SLEEP);

        // 验证当前服务器是否有活动,没有活动返回false
		if (srv_check_activity(old_activity_count)) {
            
            // 统计当前活动进程
			old_activity_count = srv_get_activity_count();
            
            // 重要 活动时执行的处理操作
			srv_master_do_active_tasks();
		} else {
            
            // 空闲时 执行的处理操作
			srv_master_do_idle_tasks();
		}
	}


suspend_thread:

    // 挂起 master thread 以在其线程槽 slot 中等待事件。
	srv_main_thread_op_info = "suspending";
	srv_suspend_thread(slot);
	os_event_wait(slot->event);

    // 不需要退出 回到方法开头继续执行处理操作
	if (srv_shutdown_state != SRV_SHUTDOWN_EXIT_THREADS) {
		goto loop;
	}

	my_thread_end();
	os_thread_exit();
	DBUG_RETURN(0);
}

3.srv_master_do_active_tasks 方法总结:

1.alter table要求Unix上的表处理程序可以在没有 select 查询后延迟删除表

2.检查重做日志文件中有足够的可重用空间,如果不够,刷新日志缓存区创建新的检查点

3. 合并插入缓存配置参数:srv_io_capacity * 5%的插入缓存数据

4.日志缓存刷新到磁盘,即使这个事务没有提交

5. 唤醒 purge_thread 线程 回收undo数据

6.LRU缓存清理:通过删除未使用的表在表缓存中腾出空间,最大扫描表长为50%

7.make a new checkpoint :只向 log file 中写入 LSN,不做脏页刷新

4.srv_master_do_active_tasks 方法如下:

/*********************************************************************//**
Perform the tasks that the master thread is supposed to do when the
server is active. There are two types of tasks. The first category is
of such tasks which are performed at each inovcation of this function.
We assume that this function is called roughly every second when the
server is active. The second category is of such tasks which are
performed at some interval e.g.: purge, dict_LRU cleanup etc. */
static
void
srv_master_do_active_tasks(void)
/*============================*/
{
	ib_time_monotonic_t	cur_time     = ut_time_monotonic();
	ib_time_monotonic_us_t	counter_time = ut_time_monotonic_us();

	/* First do the tasks that we are suppose to do at each
	invocation of this function. */

	++srv_main_active_loops;

	MONITOR_INC(MONITOR_MASTER_ACTIVE_LOOPS);

	/* 1. MySQL中的 alter table 要求Unix上的表处理程序可以在没有 
        select 查询后延迟删除表*/
	srv_main_thread_op_info = "doing background drop tables";
	row_drop_tables_for_mysql_in_background();
	MONITOR_INC_TIME_IN_MICRO_SECS(
		MONITOR_SRV_BACKGROUND_DROP_TABLE_MICROSECOND, counter_time);

	ut_d(srv_master_do_disabled_loop());

	if (srv_shutdown_state > 0) {
		return;
	}

	/* 2. 确保重做日志文件中有足够的可重用空间:
        如果不够,就刷新日志缓冲区或创建新的检查点 */
	srv_main_thread_op_info = "checking free log space";
	log_free_check();

	/* 3. Do merge 5% * srv_io_capacity of insert buffer */
	srv_main_thread_op_info = "doing insert buffer merge";
	counter_time = ut_time_monotonic_us();
	ibuf_merge_in_background(false);
	MONITOR_INC_TIME_IN_MICRO_SECS(
		MONITOR_SRV_IBUF_MERGE_MICROSECOND, counter_time);

	/* 4. 每秒刷新一次日志 */
	srv_main_thread_op_info = "flushing log";
	srv_sync_log_buffer_in_background();
	MONITOR_INC_TIME_IN_MICRO_SECS(
		MONITOR_SRV_LOG_FLUSH_MICROSECOND, counter_time);

	/* Now see if various tasks that are performed at defined
	intervals need to be performed. */

	if (srv_shutdown_state > 0) {
		return;
	}

	if (srv_shutdown_state > 0) {
		return;
	}

    // 不用的undo页数量大于 0,启动purge_thread 线程 回收脏页
    // 降低 master thread 线程的压力
	if (trx_sys->rseg_history_len > 0) {
		srv_wake_purge_thread_if_not_active();
	}

    /* 现在,查看是否需要执行按 定义的时间间隔执行 的各种任务 */
	if (cur_time % SRV_MASTER_DICT_LRU_INTERVAL == 0) {

        /* 5 LRU缓存清理:通过逐出 未使用的表 
            在LRU缓存 中腾出空间,最大扫描表长为百分之50 */
		srv_main_thread_op_info = "enforcing dict cache limit";
		ulint	n_evicted = srv_master_evict_from_table_cache(50);
		if (n_evicted != 0) {
			MONITOR_INC_VALUE(
				MONITOR_SRV_DICT_LRU_EVICT_COUNT, n_evicted);
		}
		MONITOR_INC_TIME_IN_MICRO_SECS(
			MONITOR_SRV_DICT_LRU_MICROSECOND, counter_time);
	}

	if (srv_shutdown_state > 0) {
		return;
	}

	/* 6 Make a new checkpoint :
        只向 log file 中写入 LSN,不做脏页刷新 */
	if (cur_time % SRV_MASTER_CHECKPOINT_INTERVAL == 0) {
		srv_main_thread_op_info = "making checkpoint";
		log_checkpoint(TRUE, FALSE);
		MONITOR_INC_TIME_IN_MICRO_SECS(
			MONITOR_SRV_CHECKPOINT_MICROSECOND, counter_time);
	}
}

5.srv_master_do_idle_tasks 方法总结:

1. 删除表只能发生在没有 SELECT queries 的时候

2. 确保重做日志文件中有足够的可重用空间

3. 合并 100% * srv_io_capacity 大小的 insert buffer

4. 刷新日志至磁盘,即使事务没有提交

5. 唤醒 purge_thread 线程 回收undo数据

6. 执行一次 LRU 缓存清理,扫描表长为整个表

7. 执行设置新的 checkpoint,只向log file中写入 lsn,不做脏页刷新

6. srv_master_do_idle_tasks 方法如下:

/*********************************************************************//**
Perform the tasks that the master thread is supposed to do whenever the
server is idle. We do check for the server state during this function
and if the server has entered the shutdown phase we may return from
the function without completing the required tasks.
Note that the server can move to active state when we are executing this
function but we don't check for that as we are suppose to perform more
or less same tasks when server is active. */
static
void
srv_master_do_idle_tasks(void)

    ib_time_monotonic_t	counter_time;

	++srv_main_idle_loops;

	MONITOR_INC(MONITOR_MASTER_IDLE_LOOPS);


	/* 1. MySQL中的 alter table 要求Unix上的表处理程序 
        必须在没有 select 查询后删除表*/
	counter_time = ut_time_monotonic_us();
	srv_main_thread_op_info = "doing background drop tables";
	row_drop_tables_for_mysql_in_background();
	MONITOR_INC_TIME_IN_MICRO_SECS(
		MONITOR_SRV_BACKGROUND_DROP_TABLE_MICROSECOND,
			 counter_time);

	ut_d(srv_master_do_disabled_loop());

	if (srv_shutdown_state > 0) {
		return;
	}

	/* 2. 确保重做日志文件中有足够的可重用空间:
        如果不够,就刷新日志缓冲区或创建新的检查点 */
	srv_main_thread_op_info = "checking free log space";
	log_free_check();

	/* 3. Do merge 100% * srv_io_capacity of insert buffer */
	counter_time = ut_time_monotonic_us();
	srv_main_thread_op_info = "doing insert buffer merge";
	ibuf_merge_in_background(true);
	MONITOR_INC_TIME_IN_MICRO_SECS(
		MONITOR_SRV_IBUF_MERGE_MICROSECOND, counter_time);

	if (srv_shutdown_state > 0) {
		return;
	}

    // 数据库存在需要回收的undo页,唤醒回收线程
	if (trx_sys->rseg_history_len > 0) {
		srv_wake_purge_thread_if_not_active();
	}

    /* 4. LRU内存清理:通过逐出 未使用的表 在 LRU缓存 中腾出空间,
        最大扫描表长为百分之100 */
	srv_main_thread_op_info = "enforcing dict cache limit";
	ulint	n_evicted = srv_master_evict_from_table_cache(100);
	if (n_evicted != 0) {
		MONITOR_INC_VALUE(
			MONITOR_SRV_DICT_LRU_EVICT_COUNT, n_evicted);
	}
	MONITOR_INC_TIME_IN_MICRO_SECS(
		MONITOR_SRV_DICT_LRU_MICROSECOND, counter_time);

	/* 5. 每秒刷新一次日志 */
	srv_sync_log_buffer_in_background();
	MONITOR_INC_TIME_IN_MICRO_SECS(
		MONITOR_SRV_LOG_FLUSH_MICROSECOND, counter_time);

	if (srv_shutdown_state > 0) {
		return;
	}

    /* 6. 设置新的检查点:只向 log file 中写入 LSN,不做脏页刷新 */
	srv_main_thread_op_info = "making checkpoint";
	log_checkpoint(TRUE, FALSE);
	MONITOR_INC_TIME_IN_MICRO_SECS(MONITOR_SRV_CHECKPOINT_MICROSECOND,
				       counter_time);
}

总结:

1. master thread 线程主要工作是刷新日志、保证数据的一致性、脏页的刷新、合并插入缓存、undo页回收(唤醒回收线程page thread), 

2. page cleaner Thread每隔一秒回收一次脏页数据,检查master thread线程的压力

参考:

1. 博客 https://blog.csdn.net/weixin_45437022/article/details/121525939

2. mySQL技术内幕  InnoDB存储引擎  第2版.pdf

评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值