mysql5.5.25 源码阅读---innodb 主线程工作流程

srv0src.cc

srv_master_thread(void* arg __attribute__((unused)))
{
      ## 省略各种变量声明、赋值
loop:
/*****************************************************************/
/* ---- When there is database activity by users, we cycle in this
loop */

srv_main_thread_op_info = "reserving kernel mutex";

        ## 获取bp的统计信息
buf_get_total_stat(&buf_stat);

n_ios_very_old = log_sys->n_log_ios + buf_stat.n_pages_read
+ buf_stat.n_pages_written;
mutex_enter(&kernel_mutex);

/* Store the user activity counter at the start of this loop */
old_activity_count = srv_activity_count;

mutex_exit(&kernel_mutex);

if (srv_force_recovery >= SRV_FORCE_NO_BACKGROUND) {

goto suspend_thread;
}

/* ---- We run the following loop approximately once per second
when there is database activity */

srv_last_log_flush_time = time(NULL);

/* Sleep for 1 second on entrying the for loop below the first time. */
next_itr_time = ut_time_ms() + 1000;

每1秒进行的工作
        for (i = 0; i < 10; i++) {
ulint cur_time = ut_time_ms();

/* ALTER TABLE in MySQL requires on Unix that the table handler
can drop tables lazily after there no longer are SELECT
queries to them. */
srv_main_thread_op_info = "doing background drop tables";

                ## ALTER TABLE产生的drop tables lazily信息
row_drop_tables_for_mysql_in_background();

srv_main_thread_op_info = "";

if (srv_fast_shutdown && srv_shutdown_state > 0) {
goto background_loop;
}

                ## 获取bp的统计信息
buf_get_total_stat(&buf_stat);

n_ios_old = log_sys->n_log_ios + buf_stat.n_pages_read
+ buf_stat.n_pages_written;

srv_main_thread_op_info = "sleeping";
srv_main_1_second_loops++;

                ## 可能sleep 1秒
if (next_itr_time > cur_time
    && srv_shutdown_state == SRV_SHUTDOWN_NONE) {

/* Get sleep interval in micro seconds. We use
ut_min() to avoid long sleep in case of
wrap around. */
os_thread_sleep(ut_min(1000000,
(next_itr_time - cur_time)
* 1000));
srv_main_sleeps++;
}

/* Each iteration should happen at 1 second interval. */
next_itr_time = ut_time_ms() + 1000;

                ## flush log buffer
srv_sync_log_buffer_in_background();

                ## 检查是否要进行flush log buffer 或者产生一个新的checkpoint
srv_main_thread_op_info = "making checkpoint";
                log_free_check();

/* If i/os during one second sleep were less than 5% of
capacity, we assume that there is free disk i/o capacity
available, and it makes sense to do an insert buffer merge. */

                ## 获取bp的统计信息
buf_get_total_stat(&buf_stat);

                ## 可能merge最多5个insert buffer
n_pend_ios = buf_get_n_pending_ios() + log_sys->n_pending_writes;
n_ios = log_sys->n_log_ios + buf_stat.n_pages_read
+ buf_stat.n_pages_written;
if (n_pend_ios < SRV_PEND_IO_THRESHOLD
    && (n_ios - n_ios_old < SRV_RECENT_IO_ACTIVITY)) {
ibuf_contract_for_n_pages(FALSE, PCT_IO(5));

/* Flush logs if needed */
srv_sync_log_buffer_in_background();
}

                ## 根据BP的脏页比例, 可能则flush最多100个脏页
if (UNIV_UNLIKELY(buf_get_modified_ratio_pct()
  > srv_max_buf_pool_modified_pct)) {
n_pages_flushed = buf_flush_list(
PCT_IO(100), IB_ULONGLONG_MAX);

} else if (srv_adaptive_flushing) {
ulint n_flush = buf_flush_get_desired_flush_rate();

if (n_flush) {
n_flush = ut_min(PCT_IO(100), n_flush);
n_pages_flushed =
                                      buf_flush_list(n_flush,IB_ULONGLONG_MAX);
}
}

                ## 检查是否存在活动用户
if (srv_activity_count == old_activity_count) {
goto background_loop;
}
}

        每1秒的工作结束

        每10秒的工作开始
/* ---- We perform the following code approximately once per
10 seconds when there is database activity */


        ## 获取bp的统计信息
buf_get_total_stat(&buf_stat);

n_pend_ios = buf_get_n_pending_ios() + log_sys->n_pending_writes;
n_ios = log_sys->n_log_ios + buf_stat.n_pages_read
+ buf_stat.n_pages_written;

srv_main_10_second_loops++;

        ## io使用情况, 可能则flush最多100个脏页
if (n_pend_ios < SRV_PEND_IO_THRESHOLD
    && (n_ios - n_ios_very_old < SRV_PAST_IO_ACTIVITY)) {
buf_flush_list(PCT_IO(100), IB_ULONGLONG_MAX);

                ## flush log buffer
srv_sync_log_buffer_in_background();
}

        ## merge 5个insert buffer
srv_main_thread_op_info = "doing insert buffer merge";
ibuf_contract_for_n_pages(FALSE, PCT_IO(5));

        ## flush log buffer
srv_sync_log_buffer_in_background();

        ## 未启用purge thread,则full purge。即删除BP中无用的undo页
if (srv_n_purge_threads == 0) {
srv_main_thread_op_info = "master purging";
srv_master_do_purge();
}

        ## 检查BP的脏页比例是否大于70%,flush 100或10个脏页
srv_main_thread_op_info = "flushing buffer pool pages";
if (buf_get_modified_ratio_pct() > 70) {
               ## flush BP中 100 个脏页
n_pages_flushed = buf_flush_list(PCT_IO(100), IB_ULONGLONG_MAX);
} else {
               ## flush BP中 10 个脏页
n_pages_flushed = buf_flush_list(PCT_IO(10), IB_ULONGLONG_MAX);
}

        ## 产生一个checkpoint
srv_main_thread_op_info = "making checkpoint";
log_checkpoint(TRUE, FALSE);

srv_main_thread_op_info = "reserving kernel mutex";

        每10秒的工作结束
       
        ## 检查是跳回loop还是进入background_loop
mutex_enter(&kernel_mutex);
        if (srv_activity_count != old_activity_count) {
mutex_exit(&kernel_mutex);
goto loop;
}
mutex_exit(&kernel_mutex);

background_loop:
srv_main_background_loops++;
srv_main_thread_op_info = "doing background drop tables";

        ## ALTER TABLE产生的drop tables lazily信息
n_tables_to_drop = row_drop_tables_for_mysql_in_background();
if (n_tables_to_drop > 0) {
/* Do not monopolize the CPU even if there are tables waiting
in the background drop queue. (It is essentially a bug if
MySQL tries to drop a table while there are still open handles
to it and we had to put it to the background drop queue.) */

if (srv_shutdown_state == SRV_SHUTDOWN_NONE) {
os_thread_sleep(100000);
}
}

        ## 未启用purge thread,则full purge。即删除BP中无用的undo页
if (srv_n_purge_threads == 0) {
srv_main_thread_op_info = "master purging";
srv_master_do_purge();
}

        ## 检查是跳回loop还是继续
srv_main_thread_op_info = "reserving kernel mutex";
mutex_enter(&kernel_mutex);
if (srv_activity_count != old_activity_count) {
mutex_exit(&kernel_mutex);
goto loop;
}
mutex_exit(&kernel_mutex);

        ## 根据srv_fast_shutdown配置
             是否merge最多100个insert buffer

srv_main_thread_op_info = "doing insert buffer merge";
if (srv_fast_shutdown && srv_shutdown_state > 0) {
n_bytes_merged = 0;
} else {
n_bytes_merged = ibuf_contract_for_n_pages(FALSE,
   PCT_IO(100));
}

        ## 检查是跳回loop还是进入 flush loop
mutex_enter(&kernel_mutex);
if (srv_activity_count != old_activity_count) {
mutex_exit(&kernel_mutex);
goto loop;
}
mutex_exit(&kernel_mutex);

flush_loop:
srv_main_thread_op_info = "flushing buffer pool pages";
srv_main_flush_loops++;
        ## srv_fast_shutdown<2,从LFU中flush最多100个 dirty page
if (srv_fast_shutdown < 2) {
n_pages_flushed = buf_flush_list(
  PCT_IO(100), IB_ULONGLONG_MAX);
} else {
/* In the fastest shutdown we do not flush the buffer pool
to data files: we set n_pages_flushed to 0 artificially. */

n_pages_flushed = 0;
}

        ## 检查是跳回loop还是继续
srv_main_thread_op_info = "reserving kernel mutex";
mutex_enter(&kernel_mutex);
if (srv_activity_count != old_activity_count) {
mutex_exit(&kernel_mutex);
goto loop;
}
mutex_exit(&kernel_mutex);

        ## 将LFU所有脏页进行flush,直到有flush结束信号
srv_main_thread_op_info = "waiting for buffer pool flush to end";
buf_flush_wait_batch_end(NULL, BUF_FLUSH_LIST);

        ## merge insert buffer
srv_sync_log_buffer_in_background();

        ## 产生 checkpoint
srv_main_thread_op_info = "making checkpoint";
log_checkpoint(TRUE, FALSE);

        ## 根据脏页比较是否继续flush_loop
if (buf_get_modified_ratio_pct() > srv_max_buf_pool_modified_pct) {
goto flush_loop;
}

        ## 检查是跳回loop还是继续
srv_main_thread_op_info = "reserving kernel mutex";
mutex_enter(&kernel_mutex);
if (srv_activity_count != old_activity_count) {
mutex_exit(&kernel_mutex);
goto loop;
}
mutex_exit(&kernel_mutex);

        ## log归档检查
/*
srv_main_thread_op_info = "archiving log (if log archive is on)";
log_archive_do(FALSE, &n_bytes_archived);
*/
n_bytes_archived = 0;
/* Print progress message every 60 seconds during shutdown */
if (srv_shutdown_state > 0 && srv_print_verbose_log) {
srv_shutdown_print_master_pending(&last_print_time,
  n_tables_to_drop,
  n_bytes_merged,
  n_pages_flushed);
}

        ##
             是否有ALTER TABLE产生的drop tables lazily信息
             是否有新的脏页需要flush
             是否有log需要archive
             重新跳回background_loop       
       

if (srv_fast_shutdown && srv_shutdown_state > 0) {
if (n_tables_to_drop + n_pages_flushed
    + n_bytes_archived != 0) {
goto background_loop;
}
} else if (n_tables_to_drop
   + n_pages_purged + n_bytes_merged + n_pages_flushed
   + n_bytes_archived != 0) {
goto background_loop;
}

/* There is no work for background operations either: suspend
master thread to wait for more server activity */
        ##   mysql挂起,等待事件发生
suspend_thread:
srv_main_thread_op_info = "suspending";

mutex_enter(&kernel_mutex);

if (row_get_background_drop_list_len_low() > 0) {
mutex_exit(&kernel_mutex);
goto loop;
}

srv_suspend_thread(slot);

mutex_exit(&kernel_mutex);

/* DO NOT CHANGE THIS STRING. innobase_start_or_create_for_mysql()
waits for database activity to die down when converting < 4.1.x
databases, and relies on this string being exactly as it is. InnoDB
manual also mentions this string in several places. */
srv_main_thread_op_info = "waiting for server activity";

os_event_wait(slot->event);

if (srv_shutdown_state == SRV_SHUTDOWN_EXIT_THREADS) {
os_thread_exit(NULL);
}

/* When there is user activity, InnoDB will set the event and the
main thread goes back to loop. */

goto loop;
}

猜你喜欢

转载自babaoqi.iteye.com/blog/1748052