1.master 线程的主代码位于 (1)storage/innobase/srv/srv0srv.cc (2)storage/innobase/buf/buf0flu.cc srv_master_do_idle_task()每10秒中的操作。 srv_master_do_active_tasks()每秒中的操作。 1秒中的算法做的事情(主循环): (1)日志刷新到磁盘,即使这个事务没有提交(总是) (2)合并插入缓冲(可能) //(3)至多刷新innodb_io_capacity个innodb的缓冲池中的脏页到磁盘(可能) ---innodb新版本中已经交给了page_cleaner线程 (4)如果没有用户活动,切换到background loop(可能) 10秒中的算法做的事情(主循环): (1)刷新innodb_io_capacity个脏页到从盘(可能) (2)合并至多innodb_io_capacity*5%个插入缓冲(总是) (3)将日志缓冲刷新到磁盘(总是) //(4)刷新100个或者10个脏页到磁盘(总是)---innodb新版本中已经交给了page_cleaner线程 切换到background loop做的事情: (1)删除innodb_purge_batch_size个无用的undo页(总是) (2)合并innodb_io_capacity*5%个插入缓冲(总是) (3)跳回到主循环(总是) //(4)不断刷新innodb_io_capacity个页直到符合条件---innodb新版本中已经交给了page_cleaner线程 mysql5.6后台线程: +-----------+----------------------------------------+------------+ | thread_id | name | type | +-----------+----------------------------------------+------------+ | 1 | thread/sql/main | BACKGROUND | | 2 | thread/innodb/io_handler_thread | BACKGROUND | | 3 | thread/innodb/io_handler_thread | BACKGROUND | | 4 | thread/innodb/io_handler_thread | BACKGROUND | | 5 | thread/innodb/io_handler_thread | BACKGROUND | | 6 | thread/innodb/io_handler_thread | BACKGROUND | | 7 | thread/innodb/io_handler_thread | BACKGROUND | | 8 | thread/innodb/io_handler_thread | BACKGROUND | | 9 | thread/innodb/io_handler_thread | BACKGROUND | | 10 | thread/innodb/io_handler_thread | BACKGROUND | | 11 | thread/innodb/io_handler_thread | BACKGROUND | | 14 | thread/innodb/srv_master_thread | BACKGROUND | | 15 | thread/innodb/srv_monitor_thread | BACKGROUND | | 16 | thread/innodb/srv_purge_thread | BACKGROUND | --undo页回收线程 | 17 | thread/innodb/srv_error_monitor_thread | BACKGROUND | | 18 | thread/innodb/srv_lock_timeout_thread | BACKGROUND | | 19 | thread/innodb/page_cleaner_thread | BACKGROUND | --刷新脏页线程 | 20 | thread/sql/signal_handler | BACKGROUND | +-----------+----------------------------------------+------------+ storage/innobase/buf/buf0flu.cc: /******************************************************************//** page_cleaner thread tasked with flushing dirty pages from the buffer pools. As of now we‘ll have only one instance of this thread. @return a dummy parameter */ extern "C" UNIV_INTERN os_thread_ret_t DECLARE_THREAD(buf_flush_page_cleaner_thread)( /*==========================================*/ void* arg __attribute__((unused))) /*!< in: a dummy parameter required by os_thread_create */ { ulint next_loop_time = ut_time_ms() + 1000; ulint n_flushed = 0; ulint last_activity = srv_get_activity_count(); ut_ad(!srv_read_only_mode); #ifdef UNIV_PFS_THREAD pfs_register_thread(buf_page_cleaner_thread_key); #endif /* UNIV_PFS_THREAD */ #ifdef UNIV_DEBUG_THREAD_CREATION fprintf(stderr, "InnoDB: page_cleaner thread running, id %lu\n", os_thread_pf(os_thread_get_curr_id())); #endif /* UNIV_DEBUG_THREAD_CREATION */ buf_page_cleaner_is_active = TRUE; while (srv_shutdown_state == SRV_SHUTDOWN_NONE) { /* The page_cleaner skips sleep if the server is idle and there are no pending IOs in the buffer pool and there is work to do. */ if (srv_check_activity(last_activity) || buf_get_n_pending_read_ios() || n_flushed == 0) { page_cleaner_sleep_if_needed(next_loop_time); } next_loop_time = ut_time_ms() + 1000; if (srv_check_activity(last_activity)) { last_activity = srv_get_activity_count(); /* Flush pages from end of LRU if required */ n_flushed = buf_flush_LRU_tail(); /* Flush pages from flush_list if required */ n_flushed += page_cleaner_flush_pages_if_needed(); } else { n_flushed = page_cleaner_do_flush_batch( PCT_IO(100), LSN_MAX); if (n_flushed) { MONITOR_INC_VALUE_CUMULATIVE( MONITOR_FLUSH_BACKGROUND_TOTAL_PAGE, MONITOR_FLUSH_BACKGROUND_COUNT, MONITOR_FLUSH_BACKGROUND_PAGES, n_flushed); } } } ut_ad(srv_shutdown_state > 0); if (srv_fast_shutdown == 2) { /* In very fast shutdown we simulate a crash of buffer pool. We are not required to do any flushing */ goto thread_exit; } /* In case of normal and slow shutdown the page_cleaner thread must wait for all other activity in the server to die down. Note that we can start flushing the buffer pool as soon as the server enters shutdown phase but we must stay alive long enough to ensure that any work done by the master or purge threads is also flushed. During shutdown we pass through two stages. In the first stage, when SRV_SHUTDOWN_CLEANUP is set other threads like the master and the purge threads may be working as well. We start flushing the buffer pool but can‘t be sure that no new pages are being dirtied until we enter SRV_SHUTDOWN_FLUSH_PHASE phase. */ do { n_flushed = page_cleaner_do_flush_batch(PCT_IO(100), LSN_MAX); /* We sleep only if there are no pages to flush */ if (n_flushed == 0) { os_thread_sleep(100000); } } while (srv_shutdown_state == SRV_SHUTDOWN_CLEANUP); /* At this point all threads including the master and the purge thread must have been suspended. */ ut_a(srv_get_active_thread_type() == SRV_NONE); ut_a(srv_shutdown_state == SRV_SHUTDOWN_FLUSH_PHASE); /* We can now make a final sweep on flushing the buffer pool and exit after we have cleaned the whole buffer pool. It is important that we wait for any running batch that has been triggered by us to finish. Otherwise we can end up considering end of that batch as a finish of our final sweep and we‘ll come out of the loop leaving behind dirty pages in the flush_list */ buf_flush_wait_batch_end(NULL, BUF_FLUSH_LIST); buf_flush_wait_LRU_batch_end(); bool success; do { success = buf_flush_list(PCT_IO(100), LSN_MAX, &n_flushed); buf_flush_wait_batch_end(NULL, BUF_FLUSH_LIST); } while (!success || n_flushed > 0); /* Some sanity checks */ ut_a(srv_get_active_thread_type() == SRV_NONE); ut_a(srv_shutdown_state == SRV_SHUTDOWN_FLUSH_PHASE); for (ulint i = 0; i < srv_buf_pool_instances; i++) { buf_pool_t* buf_pool = buf_pool_from_array(i); ut_a(UT_LIST_GET_LEN(buf_pool->flush_list) == 0); } /* We have lived our life. Time to die. */ thread_exit: buf_page_cleaner_is_active = FALSE; /* We count the number of threads in os_thread_exit(). A created thread should always use that to exit and not use return() to exit. */ os_thread_exit(NULL); OS_THREAD_DUMMY_RETURN; } /*********************************************************************//** This function is called approximately once every second by the page_cleaner thread. Based on various factors it decides if there is a need to do flushing. If flushing is needed it is performed and the number of pages flushed is returned. @return number of pages flushed */ static ulint page_cleaner_flush_pages_if_needed(void) /*====================================*/ { static lsn_t lsn_avg_rate = 0; static lsn_t prev_lsn = 0; static lsn_t last_lsn = 0; static ulint sum_pages = 0; static ulint last_pages = 0; static ulint prev_pages = 0; static ulint avg_page_rate = 0; static ulint n_iterations = 0; lsn_t oldest_lsn; lsn_t cur_lsn; lsn_t age; lsn_t lsn_rate; ulint n_pages = 0; ulint pct_for_dirty = 0; ulint pct_for_lsn = 0; ulint pct_total = 0; int age_factor = 0; cur_lsn = log_get_lsn(); if (prev_lsn == 0) { /* First time around. */ prev_lsn = cur_lsn; return(0); } if (prev_lsn == cur_lsn) { return(0); } /* We update our variables every srv_flushing_avg_loops iterations to smooth out transition in workload. */ if (++n_iterations >= srv_flushing_avg_loops) { avg_page_rate = ((sum_pages / srv_flushing_avg_loops) + avg_page_rate) / 2; /* How much LSN we have generated since last call. */ lsn_rate = (cur_lsn - prev_lsn) / srv_flushing_avg_loops; lsn_avg_rate = (lsn_avg_rate + lsn_rate) / 2; prev_lsn = cur_lsn; n_iterations = 0; sum_pages = 0; } oldest_lsn = buf_pool_get_oldest_modification(); ut_ad(oldest_lsn <= log_get_lsn()); age = cur_lsn > oldest_lsn ? cur_lsn - oldest_lsn : 0; pct_for_dirty = af_get_pct_for_dirty(); pct_for_lsn = af_get_pct_for_lsn(age); pct_total = ut_max(pct_for_dirty, pct_for_lsn); /* Cap the maximum IO capacity that we are going to use by max_io_capacity. */ n_pages = (PCT_IO(pct_total) + avg_page_rate) / 2; if (n_pages > srv_max_io_capacity) { n_pages = srv_max_io_capacity; } if (last_pages && cur_lsn - last_lsn > lsn_avg_rate / 2) { age_factor = prev_pages / last_pages; } MONITOR_SET(MONITOR_FLUSH_N_TO_FLUSH_REQUESTED, n_pages); prev_pages = n_pages; n_pages = page_cleaner_do_flush_batch( n_pages, oldest_lsn + lsn_avg_rate * (age_factor + 1)); last_lsn= cur_lsn; last_pages= n_pages + 1; MONITOR_SET(MONITOR_FLUSH_AVG_PAGE_RATE, avg_page_rate); MONITOR_SET(MONITOR_FLUSH_LSN_AVG_RATE, lsn_avg_rate); MONITOR_SET(MONITOR_FLUSH_PCT_FOR_DIRTY, pct_for_dirty); MONITOR_SET(MONITOR_FLUSH_PCT_FOR_LSN, pct_for_lsn); if (n_pages) { MONITOR_INC_VALUE_CUMULATIVE( MONITOR_FLUSH_ADAPTIVE_TOTAL_PAGE, MONITOR_FLUSH_ADAPTIVE_COUNT, MONITOR_FLUSH_ADAPTIVE_PAGES, n_pages); sum_pages += n_pages; } return(n_pages); } storage/innobase/srv/srv0srv.c: /*********************************************************************//** 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_t cur_time = ut_time(); ullint counter_time = ut_time_us(NULL); /* 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); /* 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"; row_drop_tables_for_mysql_in_background(); MONITOR_INC_TIME_IN_MICRO_SECS( MONITOR_SRV_BACKGROUND_DROP_TABLE_MICROSECOND, counter_time); if (srv_shutdown_state > 0) { return; } /* make sure that there is enough reusable space in the redo log files */ srv_main_thread_op_info = "checking free log space"; log_free_check(); /* Do an ibuf merge */ srv_main_thread_op_info = "doing insert buffer merge"; counter_time = ut_time_us(NULL); ibuf_contract_in_background(0, FALSE); MONITOR_INC_TIME_IN_MICRO_SECS( MONITOR_SRV_IBUF_MERGE_MICROSECOND, counter_time); /* Flush logs if needed */ 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. */ #ifdef MEM_PERIODIC_CHECK /* Check magic numbers of every allocated mem block once in SRV_MASTER_MEM_VALIDATE_INTERVAL seconds */ if (cur_time % SRV_MASTER_MEM_VALIDATE_INTERVAL == 0) { mem_validate_all_blocks(); MONITOR_INC_TIME_IN_MICRO_SECS( MONITOR_SRV_MEM_VALIDATE_MICROSECOND, counter_time); } #endif if (srv_shutdown_state > 0) { return; } if (srv_shutdown_state > 0) { return; } if (cur_time % SRV_MASTER_DICT_LRU_INTERVAL == 0) { srv_main_thread_op_info = "enforcing dict cache limit"; srv_master_evict_from_table_cache(50); MONITOR_INC_TIME_IN_MICRO_SECS( MONITOR_SRV_DICT_LRU_MICROSECOND, counter_time); } if (srv_shutdown_state > 0) { return; } /* Make a new checkpoint */ 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); } } /*********************************************************************//** 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) /*==========================*/ { ullint counter_time; ++srv_main_idle_loops; MONITOR_INC(MONITOR_MASTER_IDLE_LOOPS); /* ALTER TABLE in MySQL requires on Unix that the table handler can drop tables lazily after there no longer are SELECT queries to them. */ counter_time = ut_time_us(NULL); 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); if (srv_shutdown_state > 0) { return; } /* make sure that there is enough reusable space in the redo log files */ srv_main_thread_op_info = "checking free log space"; log_free_check(); /* Do an ibuf merge */ counter_time = ut_time_us(NULL); srv_main_thread_op_info = "doing insert buffer merge"; ibuf_contract_in_background(0, TRUE); MONITOR_INC_TIME_IN_MICRO_SECS( MONITOR_SRV_IBUF_MERGE_MICROSECOND, counter_time); if (srv_shutdown_state > 0) { return; } srv_main_thread_op_info = "enforcing dict cache limit"; srv_master_evict_from_table_cache(100); MONITOR_INC_TIME_IN_MICRO_SECS( MONITOR_SRV_DICT_LRU_MICROSECOND, counter_time); /* Flush logs if needed */ 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; } /* Make a new checkpoint */ srv_main_thread_op_info = "making checkpoint"; log_checkpoint(TRUE, FALSE); MONITOR_INC_TIME_IN_MICRO_SECS(MONITOR_SRV_CHECKPOINT_MICROSECOND, counter_time); } /*********************************************************************//** Perform the tasks during shutdown. The tasks that we do at shutdown depend on srv_fast_shutdown: 2 => very fast shutdown => do no book keeping 1 => normal shutdown => clear drop table queue and make checkpoint 0 => slow shutdown => in addition to above do complete purge and ibuf merge @return TRUE if some work was done. FALSE otherwise */ static ibool srv_master_do_shutdown_tasks( /*=========================*/ ib_time_t* last_print_time)/*!< last time the function print the message */ { ulint n_bytes_merged = 0; ulint n_tables_to_drop = 0; ut_ad(!srv_read_only_mode); ++srv_main_shutdown_loops; ut_a(srv_shutdown_state > 0); /* In very fast shutdown none of the following is necessary */ if (srv_fast_shutdown == 2) { return(FALSE); } /* 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"; n_tables_to_drop = row_drop_tables_for_mysql_in_background(); /* make sure that there is enough reusable space in the redo log files */ srv_main_thread_op_info = "checking free log space"; log_free_check(); /* In case of normal shutdown we don‘t do ibuf merge or purge */ if (srv_fast_shutdown == 1) { goto func_exit; } /* Do an ibuf merge */ srv_main_thread_op_info = "doing insert buffer merge"; n_bytes_merged = ibuf_contract_in_background(0, TRUE); /* Flush logs if needed */ srv_sync_log_buffer_in_background(); func_exit: /* Make a new checkpoint about once in 10 seconds */ srv_main_thread_op_info = "making checkpoint"; log_checkpoint(TRUE, FALSE); /* 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); } return(n_bytes_merged || n_tables_to_drop); } /*********************************************************************//** Puts master thread to sleep. At this point we are using polling to service various activities. Master thread sleeps for one second before checking the state of the server again */ static void srv_master_sleep(void) /*==================*/ { srv_main_thread_op_info = "sleeping"; os_thread_sleep(1000000); srv_main_thread_op_info = ""; } /*********************************************************************//** The master thread controlling the server. @return a dummy parameter */ extern "C" UNIV_INTERN os_thread_ret_t DECLARE_THREAD(srv_master_thread)( /*==============================*/ void* arg __attribute__((unused))) /*!< in: a dummy parameter required by os_thread_create */ { srv_slot_t* slot; ulint old_activity_count = srv_get_activity_count(); ib_time_t last_print_time; ut_ad(!srv_read_only_mode); #ifdef UNIV_DEBUG_THREAD_CREATION fprintf(stderr, "Master thread starts, id %lu\n", os_thread_pf(os_thread_get_curr_id())); #endif /* UNIV_DEBUG_THREAD_CREATION */ #ifdef UNIV_PFS_THREAD pfs_register_thread(srv_master_thread_key); #endif /* UNIV_PFS_THREAD */ srv_main_thread_process_no = os_proc_get_number(); srv_main_thread_id = os_thread_pf(os_thread_get_curr_id()); slot = srv_reserve_slot(SRV_MASTER); ut_a(slot == srv_sys->sys_threads); last_print_time = ut_time(); loop: if (srv_force_recovery >= SRV_FORCE_NO_BACKGROUND) { goto suspend_thread; } while (srv_shutdown_state == SRV_SHUTDOWN_NONE) { srv_master_sleep(); MONITOR_INC(MONITOR_MASTER_THREAD_SLEEP); 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(); } } while (srv_master_do_shutdown_tasks(&last_print_time)) { /* Shouldn‘t loop here in case of very fast shutdown */ ut_ad(srv_fast_shutdown < 2); } suspend_thread: srv_main_thread_op_info = "suspending"; srv_suspend_thread(slot); /* 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); } goto loop; OS_THREAD_DUMMY_RETURN; /* Not reached, avoid compiler warning */ }
时间: 2024-10-24 19:51:33