您好,登錄后才能下訂單哦!
這篇文章主要介紹MySQL半同步復制的示例分析,文中介紹的非常詳細,具有一定的參考價值,感興趣的小伙伴們一定要看完!
代碼分析
int repl_semi_report_commit(Trans_param *param)//gdb下param?
{
bool is_real_trans= param->flags & TRANS_IS_REAL_TRANS;
if (is_real_trans && param->log_pos)
{
const char *binlog_name= param->log_file;
return repl_semisync.commitTrx(binlog_name, param->log_pos);
}
return 0;
}
<ol start="1" class="dp-cpp" white-space:normal;margin:0px 0px 1px 45px !important;">
int ReplSemiSyncMaster::commitTrx(const char* trx_wait_binlog_name,
my_off_t trx_wait_binlog_pos)
{
//自旋鎖,下面的代碼是線性執行。
mysql_mutex_lock(&LOCK_binlog_);
if (active_tranxs_ != NULL && trx_wait_binlog_name){
entry=active_tranxs_->find_active_tranx_node(trx_wait_binlog_name,
trx_wait_binlog_pos);
if (entry)
thd_cond= &entry->cond;
}
//進入信號了,為后面發起信號量的等待動作做準備,每個正在進行提交的事務都對應一個初始化的信號量thd_cond
THD_ENTER_COND(NULL, thd_cond, &LOCK_binlog_,
& stage_waiting_for_semi_sync_ack_from_slave,
& old_stage);
if (getMasterEnabled() && trx_wait_binlog_name){
set_timespec(start_ts, 0);//
if (!getMasterEnabled() || !is_on())
goto l_end;
//計算等待ACK的截止時間。按照當前時間加上半同步等待的超時時間,這個時間回在發起信號量等待的時候用的
//rpl_semi_sync_master_timeout
abstime.tv_sec = start_ts.tv_sec + wait_timeout_ / TIME_THOUSAND;
abstime.tv_nsec = start_ts.tv_nsec +(wait_timeout_ % TIME_THOUSAND) * TIME_MILLION;
if (abstime.tv_nsec >= TIME_BILLION){
abstime.tv_sec++;
abstime.tv_nsec -= TIME_BILLION;
}
//state_是TRUE表示當前半同步狀態為on,否則直接進入l_end。Rpl_semi_sync_master_status
//reply_file_name_值的變化,在其他函數中?
while (is_on()){
if (reply_file_name_inited_){
//比較事務所涉及的binlog位置跟reply的位置,如果cmp>0,說明此事務的binlog已經同步
//到slave,跳出該循環,進入最后階段l_end
int cmp = ActiveTranx::compare(reply_file_name_, reply_file_pos_,
trx_wait_binlog_name, trx_wait_binlog_pos);
if (cmp >= 0){
break;
}
}
if (wait_file_name_inited_){
//比較事務所涉及的binlog位置和當前最小需要等待的binlog位置。如果cmp<0,表示調整當前最小需要等待
//binlog的位置。rpl_semi_sync_master_wait_pos_backtraverse++,即等待位置需要調整的次數,一般不會
//調整
int cmp = ActiveTranx::compare(trx_wait_binlog_name, trx_wait_binlog_pos,
wait_file_name_, wait_file_pos_);
if (cmp <= 0){
strncpy(wait_file_name_, trx_wait_binlog_name, sizeof(wait_file_name_) - 1);
wait_file_name_[sizeof(wait_file_name_) - 1]= '\0';
wait_file_pos_ = trx_wait_binlog_pos;
rpl_semi_sync_master_wait_pos_backtraverse++;
}
}else{
//保存第一次最小需要響應的事務位置
strncpy(wait_file_name_, trx_wait_binlog_name, sizeof(wait_file_name_) - 1);
wait_file_name_[sizeof(wait_file_name_) - 1]= '\0';
wait_file_pos_ = trx_wait_binlog_pos;
wait_file_name_inited_ = true;
}
//如果salve個數是0了,則將半同步關閉,退出循環
if (abort_loop && rpl_semi_sync_master_clients == 0 && is_on()){
switch_off();
break;
}
//正式進入等待binlog同步的步驟,將rpl_semi_sync_master_wait_sessions+1,表明
//有多少要提交的事務線程在等待(這個值是否能夠代表實際等待事務的線程數量,值得懷疑,因為該函數
//開始位置有lock,沒有unlock前,其他線程也進不到這一步,沒辦法執行++)
//然后發起等待信號,進入信號等待后,只有2種情況可以退出等待。1是被其他線程喚醒(binlog dump)
//2是等待超時時間。如果是被喚醒則返回值是0,否則是其他值
rpl_semi_sync_master_wait_sessions++;
entry->n_waiters++;
//發起信號等待,然后根據返回結果做相應計數:上面是循環體里面的所有內容,接下來我們看退出循環后的操作。特別提一下,喚醒該線程的dump線程,當dump線程收到相應binlog位置的ack之后,會將其喚醒。
wait_result= mysql_cond_timedwait(&entry->cond, &LOCK_binlog_, &abstime);
entry->n_waiters--;
rpl_semi_sync_master_wait_sessions--;
if (wait_result != 0){
//等待超時,關閉半同步
rpl_semi_sync_master_wait_timeouts++;
switch_off();
}else{
wait_time = getWaitTime(start_ts);
if (wait_time < 0){
//表明時鐘錯誤,可能是做了時間調整
rpl_semi_sync_master_timefunc_fails++;
}else{
//將等待事件與該等待計入總數
rpl_semi_sync_master_trx_wait_num++;
rpl_semi_sync_master_trx_wait_time += wait_time;
}
}
}//end while
l_end:
/* Update the status counter. */
if (is_on())
rpl_semi_sync_master_yes_transactions++;
else
rpl_semi_sync_master_no_transactions++;
}
/* Last waiter removes the TranxNode */
if (trx_wait_binlog_name && active_tranxs_
&& entry && entry->n_waiters == 0)
active_tranxs_->clear_active_tranx_nodes(trx_wait_binlog_name,
trx_wait_binlog_pos);
THD_EXIT_COND(NULL, & old_stage);
}
1)在commit函數中,首先需要加一個自旋鎖LOCK_binlog_,主要動作都在這個鎖內執行。
2)進入信號,為后面發起信號量的等待動作做準備
3)計算binlog等待ACK的截止時間。從此時開始+半同步等待的超時時間rpl_semi_sync_master_timeout(默認是10s)
4)需要在半同步狀態下進入下面操作,否則進入l_end。半同步狀態的判斷是state_,和Rpl_semi_sync_master_status是什么關系?
5)第一次進來:保存最小需要響應的事務位置wait_file_name_、wait_file_pos_,并將wait_file_name_inited_置成TRUE
6)最大響應位置reply_file_name_、reply_file_pos_在binlog dump線程修改,和當前binlog(已經flush的?)的位置比較。若當前binlog位置比reply的小,表示次事務的binlog已經到slave了,跳出循環,進入最后階段l_end
7)非第一次進來:比較事務涉及的binlog位置和當前最小需要等待的binlog位置。如果比wai_file_name的小,需要將最小需要等待的位置調整到當前位置。rpl_semi_sync_master_wait_pos_backtraverse++,即最小等待位置需要調整的次數。一般不會調整。
8)第一次進來:需要保存最小需要等待響應的位置為當前位置
9)接著,需要判斷slave個數和半同步是否正常。不正常則退出循環,將半同步關閉
10)正式進入等待binlog同步的步驟:
rpl_semi_sync_master_wait_sessions+1:表示有多少提交的事務線程正在等待
發起信號等待:mysql_cond_timedwait:只有2中情況可以退出等待:1是被其他線程binlog dump喚醒,2是等待超時。
特別提一下,喚醒該線程的dump線程,當dump線程收到相應binlog位置的ack之后,會將其喚醒。
等待超時:將半同步關閉
接收到slave ACK,被binlog dump線程喚醒:修改對應變量
11)將after_flush步驟插入active_trans的node刪掉
12)直到最后一步才釋放鎖,因此該函數是整個實例串行的。同時中間有個信號等待的動作。如果數據庫并發量很大,而此時主從異常,一旦超時時間設置過大,則可能出現其他用戶線程阻塞在lock()函數上,杜塞時間越長,累積的線程越多,容易引發雪崩,所以超時時間設置需謹慎,并非隨意設置。
以上是“MySQL半同步復制的示例分析”這篇文章的所有內容,感謝各位的閱讀!希望分享的內容對大家有幫助,更多相關知識,歡迎關注億速云行業資訊頻道!
免責聲明:本站發布的內容(圖片、視頻和文字)以原創、轉載和分享為主,文章觀點不代表本網站立場,如果涉及侵權請聯系站長郵箱:is@yisu.com進行舉報,并提供相關證據,一經查實,將立刻刪除涉嫌侵權內容。