您好,登錄后才能下訂單哦!
這篇文章主要介紹InnoDB IO路徑源碼的示例分析,文中介紹的非常詳細,具有一定的參考價值,感興趣的小伙伴們一定要看完!
InnoDB實現IO Flush通過“os_file_flush”宏收斂,macro展開后為”os_file_flush_func”。
接下來,我們重點看一下,還有其它哪些場景會調用到這個os_file_flush_func函數:
1. buf_dblwr_init_or_load_pages中,在double buffer 做crash恢復時,如果設置了reset_space_ids為”true”
2. fil_create_new_single_table_tablespace中,創建表數據文件時
3. fil_user_tablespace_restore_page中,從double writer buffer中copy page時
4. fil_tablespace_iterate中,如果iterate page失敗時,會做一次sync
5. os_file_set_size中,在文件新創建時初始化文件大小時
從上面可以看出,直接調用os_file_flush_func還是非常少的。那么系統中還有一些是調用os_file_flush_func的上層函數fil_flush:
1. buf_dblwr_flush_buffered_writes中,flush double write buffer到disk
2. buf_dblwr_write_single_page中,單頁flush到disk中
3. buf_flush_write_block_low中,flush block到disk中,一般在調double write buffer“buf_dblwr_flush_buffered_writes“之后調用
4. buf_LRU_remove_pages中,delete某個tablespace中的page時,做sync調用
5. fil_rename_tablespace中,rename一個tablespace時調用
6. fil_extend_space_to_desired_size中,extend space時
7. fil_flush_file_spaces中,flush 批量tablespace的page時,被ha_innodb的force checkpoint時調用,shutdown DB時調用等
8. log_io_complete中,log io完成時,checkpoint write以及其它,是否調用受sync方式影響,非commit依賴
9. log_write_up_to中,commit事務時,這個是重點,innodb 的log file都會調用,而且是同步
10. create_log_files_rename中,rename log file時
11. innobase_start_or_create_for_mysql中,創建新的文件時
在fil_flush函數中,所有文件的flush cache行為受fil_system->mutex保護。因此不管是data file還是log file,文件級別的flush是串化的。那么具體是怎么來控制的呢?
1. 檢查file node中的n_pending_flushes,如果大于“0”就一直retry
2. 如果為“0”,進入flush階段,在正式開始flush之前,先將n_pending_flushe加“1”,這個操作受上文提到的“fil->mutex“保護
3. 調用“os_file_flush“ flush完成之后,n_pending_flushes減“1”,也同樣由“fil->mutex“保護
下面是MySQL中retry的代碼:
retry: if (node->n_pending_flushes > 0) { /* We want to avoid calling os_file_flush() on the file twice at the same time, because we do not know what bugs OS's may contain in file i/o */ ib_int64_t sig_count = os_event_reset(node->sync_event); mutex_exit(&fil_system->mutex); os_event_wait_low(node->sync_event, sig_count); mutex_enter(&fil_system->mutex); if (node->flush_counter >= old_mod_counter) { goto skip_flush; } goto retry; } |
下面是MySQL中flush的代碼:
ut_a(node->open); file = node->handle; node->n_pending_flushes++; mutex_exit(&fil_system->mutex); os_file_flush(file); mutex_enter(&fil_system->mutex); os_event_set(node->sync_event); node->n_pending_flushes--; node->flush_size = node->size; |
上面提到的邏輯,對于log file也同樣處理。Log file的tablespace為log space。對于log file,還受到log_sys->mutex的保護,在log_write_up_to函數中。
Log file的flush行為收斂到“log_write_up_to”函數體中,再調用fil_flush,最終走到os_file_flush_func。
MySQL在commit的時候,如果”innodb_flush_log_at_trx_commit=1”時,調用兩次同步的log_write_up_to,一次是innobase log file 的flush,一次是bin log的flush。
如果關掉bin log,則在ordered_commit函數中,不會走sync_blog分支。以下是關掉和不關掉時,innobase flush log file的執行路徑。
不帶binlog時的Innobase 的pstack如下:
fsync,os_file_fsync,os_file_flush_func,fil_flush,log_write_up_to,trx_flush_log_if_needed_low,trx_flush_log_if_needed,trx_commit_complete_for_mysql,innobase_commit,ha_commit_low,TC_LOG_DUMMY::commit,ha_commit_trans,trans_commit_stmt,mysql_execute_command,mysql_parse,dispatch_command,do_command,do_handle_one_connection,handle_one_connection,start_thread,clone |
帶Binlog的flush pstack如下:
fsync,os_file_fsync,os_file_flush_func,fil_flush,log_write_up_to,innobase_flush_logs,flush_handlerton,plugin_foreach_with_mask,ha_flush_logs,MYSQL_BIN_LOG::process_flush_stage_queue,MYSQL_BIN_LOG::ordered_commit,MYSQL_BIN_LOG::commit,ha_commit_trans,trans_commit_stmt,mysql_execute_command,mysql_parse,dispatch_command,do_command,do_handle_one_connection,handle_one_connection,start_thread,clone |
從上面大致的調用來看,log_write_up_to()和log_io_complete()將是重點,因為這些flush在file級別是串行的,commit時的rt主要由這些串化帶來。
接下來我們看一下log_write_up_to的調用者都有那些:
1. buf_flush_write_block_low,在force flush中調用,保證日志必須先于數據落地,刷臟頁時,由page_cleaner_do_flush_batch()發起調用到此函數
2. innobase_flush_logs,在commit時調用,主要由flush binlog分支調用
3. trx_flush_log_if_needed_low,在commit時調用,主要由flush innodb log file時調用
4. log_buffer_flush_to_disk,log buffer刷日志到disk
5. log_buffer_sync_in_background,后臺線程同步log buffer到disk,由srv_master_thread 線程調srv_sync_log_buffer_in_background()調用到,每秒一次
6. log_flush_margin,主要為騰挪log buffer空間時調用
7. log_checkpoint,主要在做checkpoint時調用到,由srv_master線程調用srv_master_do_idle_tasks(),每秒做一次
log_io_complete()函數的調用情況:
1. fil_aio_wait,aio wait中如果是log io將會調用此方法
從上面分析可以看到,主要影響RT比較嚴重的還是因為刷臟頁導致的log_sys->mutex爭用。另外,log_buffer_sync_in_background和log_checkpoint,這兩個都是由后臺srv_master_thread線程每隔一秒調用到。
但是這兩個方法不一定會執行fil_flush,所以不是影響的主因。gdb掛上去后,大致會走到fil_flush爭用的pstack如下:
因此,總結起來,應該是commit做的兩次fsync加上一次page cleaner做的log_write_up_to()。另外,還有一個fil_aio_wait完成時,如果是log io,就會做一次log_io_complete()。
這四次fsync都會對用戶的rt有影響,commit的兩次無可避免,后面兩次最多也就是調整頻率。另外是否可以改變fsync的方式?這個讀者可以思考。
比oracle實現差,oracle不會作強制的,我記得是給一個標記,策略還是按redo自己的策略來做。oracle的實現應該是考慮到這一點了.
以上是“InnoDB IO路徑源碼的示例分析”這篇文章的所有內容,感謝各位的閱讀!希望分享的內容對大家有幫助,更多相關知識,歡迎關注億速云行業資訊頻道!
免責聲明:本站發布的內容(圖片、視頻和文字)以原創、轉載和分享為主,文章觀點不代表本網站立場,如果涉及侵權請聯系站長郵箱:is@yisu.com進行舉報,并提供相關證據,一經查實,將立刻刪除涉嫌侵權內容。