這篇文章主要介紹MySQL半同步復(fù)制的示例分析,文中介紹的非常詳細(xì),具有一定的參考價(jià)值,感興趣的小伙伴們一定要看完!
創(chuàng)新互聯(lián)公司是專業(yè)的金塔網(wǎng)站建設(shè)公司,金塔接單;提供成都做網(wǎng)站、成都網(wǎng)站制作,網(wǎng)頁(yè)設(shè)計(jì),網(wǎng)站設(shè)計(jì),建網(wǎng)站,PHP網(wǎng)站建設(shè)等專業(yè)做網(wǎng)站服務(wù);采用PHP框架,可快速的進(jìn)行金塔網(wǎng)站開發(fā)網(wǎng)頁(yè)制作和功能擴(kuò)展;專業(yè)做搜索引擎喜愛的網(wǎng)站,專業(yè)的做網(wǎng)站團(tuán)隊(duì),希望更多企業(yè)前來(lái)合作!
代碼分析
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;
}
int ReplSemiSyncMaster::commitTrx(const char* trx_wait_binlog_name,
my_off_t trx_wait_binlog_pos)
{
//自旋鎖,下面的代碼是線性執(zhí)行。
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;
}
//進(jìn)入信號(hào)了,為后面發(fā)起信號(hào)量的等待動(dòng)作做準(zhǔn)備,每個(gè)正在進(jìn)行提交的事務(wù)都對(duì)應(yīng)一個(gè)初始化的信號(hào)量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;
//計(jì)算等待ACK的截止時(shí)間。按照當(dāng)前時(shí)間加上半同步等待的超時(shí)時(shí)間,這個(gè)時(shí)間回在發(fā)起信號(hào)量等待的時(shí)候用的
//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表示當(dāng)前半同步狀態(tài)為on,否則直接進(jìn)入l_end。Rpl_semi_sync_master_status
//reply_file_name_值的變化,在其他函數(shù)中?
while (is_on()){
if (reply_file_name_inited_){
//比較事務(wù)所涉及的binlog位置跟reply的位置,如果cmp>0,說明此事務(wù)的binlog已經(jīng)同步
//到slave,跳出該循環(huán),進(jìn)入最后階段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_){
//比較事務(wù)所涉及的binlog位置和當(dāng)前最小需要等待的binlog位置。如果cmp<0,表示調(diào)整當(dāng)前最小需要等待
//binlog的位置。rpl_semi_sync_master_wait_pos_backtraverse++,即等待位置需要調(diào)整的次數(shù),一般不會(huì)
//調(diào)整
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{
//保存第一次最小需要響應(yīng)的事務(wù)位置
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個(gè)數(shù)是0了,則將半同步關(guān)閉,退出循環(huán)
if (abort_loop && rpl_semi_sync_master_clients == 0 && is_on()){
switch_off();
break;
}
//正式進(jìn)入等待binlog同步的步驟,將rpl_semi_sync_master_wait_sessions+1,表明
//有多少要提交的事務(wù)線程在等待(這個(gè)值是否能夠代表實(shí)際等待事務(wù)的線程數(shù)量,值得懷疑,因?yàn)樵摵瘮?shù)
//開始位置有l(wèi)ock,沒有unlock前,其他線程也進(jìn)不到這一步,沒辦法執(zhí)行++)
//然后發(fā)起等待信號(hào),進(jìn)入信號(hào)等待后,只有2種情況可以退出等待。1是被其他線程喚醒(binlog dump)
//2是等待超時(shí)時(shí)間。如果是被喚醒則返回值是0,否則是其他值
rpl_semi_sync_master_wait_sessions++;
entry->n_waiters++;
//發(fā)起信號(hào)等待,然后根據(jù)返回結(jié)果做相應(yīng)計(jì)數(shù):上面是循環(huán)體里面的所有內(nèi)容,接下來(lái)我們看退出循環(huán)后的操作。特別提一下,喚醒該線程的dump線程,當(dāng)dump線程收到相應(yīng)binlog位置的ack之后,會(huì)將其喚醒。
wait_result= mysql_cond_timedwait(&entry->cond, &LOCK_binlog_, &abstime);
entry->n_waiters--;
rpl_semi_sync_master_wait_sessions--;
if (wait_result != 0){
//等待超時(shí),關(guān)閉半同步
rpl_semi_sync_master_wait_timeouts++;
switch_off();
}else{
wait_time = getWaitTime(start_ts);
if (wait_time < 0){
//表明時(shí)鐘錯(cuò)誤,可能是做了時(shí)間調(diào)整
rpl_semi_sync_master_timefunc_fails++;
}else{
//將等待事件與該等待計(jì)入總數(shù)
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函數(shù)中,首先需要加一個(gè)自旋鎖LOCK_binlog_,主要?jiǎng)幼鞫荚谶@個(gè)鎖內(nèi)執(zhí)行。
2)進(jìn)入信號(hào),為后面發(fā)起信號(hào)量的等待動(dòng)作做準(zhǔn)備
3)計(jì)算binlog等待ACK的截止時(shí)間。從此時(shí)開始+半同步等待的超時(shí)時(shí)間rpl_semi_sync_master_timeout(默認(rèn)是10s)
4)需要在半同步狀態(tài)下進(jìn)入下面操作,否則進(jìn)入l_end。半同步狀態(tài)的判斷是state_,和Rpl_semi_sync_master_status是什么關(guān)系?
5)第一次進(jìn)來(lái):保存最小需要響應(yīng)的事務(wù)位置wait_file_name_、wait_file_pos_,并將wait_file_name_inited_置成TRUE
6)最大響應(yīng)位置reply_file_name_、reply_file_pos_在binlog dump線程修改,和當(dāng)前binlog(已經(jīng)flush的?)的位置比較。若當(dāng)前binlog位置比reply的小,表示次事務(wù)的binlog已經(jīng)到slave了,跳出循環(huán),進(jìn)入最后階段l_end
7)非第一次進(jìn)來(lái):比較事務(wù)涉及的binlog位置和當(dāng)前最小需要等待的binlog位置。如果比wai_file_name的小,需要將最小需要等待的位置調(diào)整到當(dāng)前位置。rpl_semi_sync_master_wait_pos_backtraverse++,即最小等待位置需要調(diào)整的次數(shù)。一般不會(huì)調(diào)整。
8)第一次進(jìn)來(lái):需要保存最小需要等待響應(yīng)的位置為當(dāng)前位置
9)接著,需要判斷slave個(gè)數(shù)和半同步是否正常。不正常則退出循環(huán),將半同步關(guān)閉
10)正式進(jìn)入等待binlog同步的步驟:
rpl_semi_sync_master_wait_sessions+1:表示有多少提交的事務(wù)線程正在等待
發(fā)起信號(hào)等待:mysql_cond_timedwait:只有2中情況可以退出等待:1是被其他線程binlog dump喚醒,2是等待超時(shí)。
特別提一下,喚醒該線程的dump線程,當(dāng)dump線程收到相應(yīng)binlog位置的ack之后,會(huì)將其喚醒。
等待超時(shí):將半同步關(guān)閉
接收到slave ACK,被binlog dump線程喚醒:修改對(duì)應(yīng)變量
11)將after_flush步驟插入active_trans的node刪掉
12)直到最后一步才釋放鎖,因此該函數(shù)是整個(gè)實(shí)例串行的。同時(shí)中間有個(gè)信號(hào)等待的動(dòng)作。如果數(shù)據(jù)庫(kù)并發(fā)量很大,而此時(shí)主從異常,一旦超時(shí)時(shí)間設(shè)置過大,則可能出現(xiàn)其他用戶線程阻塞在lock()函數(shù)上,杜塞時(shí)間越長(zhǎng),累積的線程越多,容易引發(fā)雪崩,所以超時(shí)時(shí)間設(shè)置需謹(jǐn)慎,并非隨意設(shè)置。
以上是“MySQL半同步復(fù)制的示例分析”這篇文章的所有內(nèi)容,感謝各位的閱讀!希望分享的內(nèi)容對(duì)大家有幫助,更多相關(guān)知識(shí),歡迎關(guān)注創(chuàng)新互聯(lián)行業(yè)資訊頻道!