在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);