從源碼解讀Mysql 5.7性能和數據安全性的提高

源碼解析html

MYSQL_BIN_LOG::ordered_commit,這個函數,核心步驟以下:mysql

第一步驟:flushlinux

Stage#1: flushing transactions to binary log:sql

步驟1 :將事務的日誌寫入binlog文件的buffer中,函數以下:數據庫

process_flush_stage_queue(&total_bytes,&do_rotate, &wait_queue);安全

從5.6開始,mysql引入了group commit 的概念,這樣能夠避免每一個事務提交都會鎖定一次binlog.另外,還有一個用處,就是mysql的5.7的基於logical_clock的並行複製。在一個組裏面(實際上是一個隊列),這一組隊列的頭事務是相同的,所以這一組事務的last_committed(上一組的最後一個提交的事務)的事務也是同一個。咱們都知道,last_committed相同的事務,是能夠在從庫並行relay(重演)的。該函數process_flush_stage_queue的做用,就是將commit隊列中的線程一個一個的取出,而後執行子函數 flush_thread_caches(head);循環的代碼以下:將各自線程中的binlog cache寫入到binlog中。異步

/* Flush thread caches to binary log. */

for (THD *head= first_seen ; head ; head = head->next_to_commit)

{

std::pair<int,my_off_t>result= flush_thread_caches(head);

total_bytes+= result.second;

if(flush_error == 1)

flush_error= result.first;

#ifndef DBUG_OFF

no_flushes++;

#endif

}

第二步驟SYNC to disk函數

Stage#2: Syncing binary log file to disk源碼分析

第二步:將binlog file中cache的部分寫入disk.但這個步驟參數sync_binlog起決定性的做用。性能

咱們來看看源碼,除了這些還有哪些細節步驟,看完源碼分析以後,你應該有新的收穫與理解。

在執行真正的將binlog寫到磁盤以前,會進行一個等待,函數以下:

stage_manager.wait_count_or_timeout(opt_binlog_group_commit_sync_no_delay_count,

opt_binlog_group_commit_sync_delay,

Stage_manager::SYNC_STAGE);

等待的時間由mysql參數文件中的binlog_group_commit_sync_delay,binlog_group_commit_sync_no_delay_count 這兩參數共同決定,第一個表示該事務組提交以前總共等待累積到多少個事務,第二個參數則表示該事務組總共等待多長時間後進行提交,任何一個條件知足則進行後續操做。由於有這個等待,可讓更多事務的binlog經過一次寫binlog文件磁盤來完成提交,從而得到更高的吞吐量。

接下來,就是執行sync_binlog_file,該函數會用到mysql參數文件中sync_binlog參數的值,若是爲0,則不進行寫磁盤操做,由操做系統決定何時刷盤,若是爲1,則強制進行寫磁盤操做。

再接下來,執行update_binlog_end_pos函數,用來更新binlog文件的最後的位置binlog_end_pos,該binlog_end_pos是一個全局的變量。在執行更新該位置以前,先得找到最後一個提交事務的線程(由於是group commit,多個事務排隊提交的機制)。由於已經將要提交事務的線程組成了一個鏈表,經過從頭至尾找,能夠找到最後一個線程。代碼以下:

if(update_binlog_end_pos_after_sync)

{

THD*tmp_thd= final_queue;

while(tmp_thd->next_to_commit != NULL)

tmp_thd= tmp_thd->next_to_commit;

update_binlog_end_pos(tmp_thd->get_trans_pos());

}

接下來,咱們來看一下這個函數update_binlog_end_pos,這個函數很簡單,傳入一個pos,而後將其賦值給全局變量binlog_end_pos,接下來就是最核心的一行代碼,signal_update(),發送binlog更新的信號,所以從主庫同步binlog到從庫的dump線程,會接收到這個binlog已有更新的信號,而後啓動dump binlog的流程。

函數update_binlog_end_pos的完整代碼以下。

semisync

經過上面的步驟介紹,咱們看到,在binlog文件的最新位置更新的時候,就已經經過signal_update函數發送信號給binlog的dump線程,該線程就能夠將事務的binlog同步到從庫,從庫接收到日誌以後,就能夠relay日誌,實現了主從同步。所以,再次重複說明一下,按照上面的解釋,在事務真正提交完成以前就開始發送了binlog已經更新的信號,dump線程收到信號,便可以進行binlog的同步。而semisync的做用是什麼呢?

實際上,有沒有semisync機制,上面介紹的mysql的有關事務提交中關於binlog的流程都是同樣的,semisync的做用,只是主從之間的一個確認過程,主庫等待從庫返回相關位置的binlog已經同步到從庫的確認,(而實際實現則是等待dump線程給用戶會話線程一個回覆),沒有獲得確認以前(或者等待時間達到timeout),事務提交則在該函數(步驟)上等待直至得到返回。具體執行binlog已經同步到某個位置的的確認函數爲repl_semi_report_binlog_sync,函數以下:

int repl_semi_report_binlog_sync(Binlog_storage_param *param,

constchar *log_file,

my_off_t log_pos)

{

if(rpl_semi_sync_master_wait_point == WAIT_AFTER_SYNC)

return repl_semisync.commitTrx(log_file, log_pos);

return 0;

}

經過觀察上述函數,咱們能夠看到有個rpl_semi_sync_master_wait_point變量與WAIT_AFTER_SYNC比較,若是不相等,則直接返回,直接返回則表示不須要在此時此刻確認binlog是否已經同步,而這個變量的取值來自於半同步參數semi_sync_master_wait_point的初始設置,咱們能夠設置爲after_sync與after_commit。這兩個參數含義的區別是:after_sync是在將binlog sync到disk以後(具體是否真正sync由參數sync_binlog的值決定)進行日誌同步確認,而after_commit是將事務完成在innodb裏面提交以後再進行binlog的同步確認。二者確認的時間點不一樣,after_sync要早於after_commit.

接下來,咱們來看repl_semisync.commitTrx 這個函數,這個函數有兩個傳入參數,一個是binlog文件,一個binlog文件的位移。咱們來看這個函數的含義吧。算了,仍是直接用源碼的註釋來解釋吧。

從源碼解讀Mysql 5.7性能和數據安全性的提高從源碼解讀Mysql 5.7性能和數據安全性的提高

上面的註釋說得至關清楚,就是該commiTRX函數會等待binlog-dump返回已經同步到該位置的報告,若是尚未同步到該位置,則繼續等待,直到超時返回。

當會話線程收到該函數的返回時,事務的提交過程繼續往下走,直至在innodb真正提交。

總結

經過上述對mysql的事務提交過程當中的前段分析,應該能夠了解semi-sync的同步機制與異步機制的區別,semi-sync的主從同步機制與異步機制在同步的處理方式上無任何區別,惟一的區別就是semi-sync在事務提交中段(假如設置爲after_sync)或者提交後的階段(after_commit), 有一個驗證該事務涉及的binlog是否已經同步到從庫,而這個同步驗證,會拉長整個事務的提交時間,由於事務提交在數據庫中幾乎是串行(若是按group commit爲一個單位,就算是徹底地串行),是影響mysql吞吐量的關鍵點,當這個關鍵點被拉長,因此對全局的影響就被放大。雖然僅僅多了這麼一個確認的動做,但當主庫處於semisync的同步狀態與異步狀態的吞吐量相比,相差了好幾倍。上述解釋就是其真正的緣由。

本文地址:http://www.linuxprobe.com/mysql5-performance-improvement.html

相關文章
相關標籤/搜索