本篇內(nèi)容主要講解“從庫(kù)的SQL線(xiàn)程和sql_slave_skip_counter參數(shù)分析”,感興趣的朋友不妨來(lái)看看。本文介紹的方法操作簡(jiǎn)單快捷,實(shí)用性強(qiáng)。下面就讓小編來(lái)帶大家學(xué)習(xí)“從庫(kù)的SQL線(xiàn)程和sql_slave_skip_counter參數(shù)分析”吧!
網(wǎng)站的建設(shè)創(chuàng)新互聯(lián)建站專(zhuān)注網(wǎng)站定制,經(jīng)驗(yàn)豐富,不做模板,主營(yíng)網(wǎng)站定制開(kāi)發(fā).小程序定制開(kāi)發(fā),H5頁(yè)面制作!給你煥然一新的設(shè)計(jì)體驗(yàn)!已為成都雨棚定制等企業(yè)提供專(zhuān)業(yè)服務(wù)。
handle_slave_sql ->是否開(kāi)啟了slave_preserve_commit_order和log_slave_updates參數(shù),開(kāi)啟的話(huà)需要設(shè)置提交順序管理器 if (opt_slave_preserve_commit_order && rli->opt_slave_parallel_workers > 0 && opt_bin_log && opt_log_slave_updates) commit_order_mngr= new Commit_order_manager(rli->opt_slave_parallel_workers); //order commit 管理器 rli->set_commit_order_manager(commit_order_mngr); ->如果是MTS則需要啟動(dòng)worker線(xiàn)程 if (slave_start_workers(rli, rli->opt_slave_parallel_workers, &mts_inited) != 0)//啟動(dòng)worker線(xiàn)程 { MySQL_cond_broadcast(&rli->start_cond); mysql_mutex_unlock(&rli->run_lock); rli->report(ERROR_LEVEL, ER_SLAVE_FATAL_ERROR, ER(ER_SLAVE_FATAL_ERROR), "Failed during slave workers initialization"); goto err; ->檢查rep table是否是事務(wù)類(lèi)型的如果不是則報(bào)警告 if (!rli->is_transactional()) //是否是 table或者是file類(lèi)型是table類(lèi)型則支持事物 rli->report(WARNING_LEVEL, 0, "If a crash happens this configuration does not guarantee that the relay " "log info will be consistent"); -> 初始化 relay log 的訪(fǎng)問(wèn)位置 if (rli->init_relay_log_pos(rli->get_group_relay_log_name(), rli->get_group_relay_log_pos(), true/*need_data_lock=true*/, &errmsg, 1 /*look for a description_event*/)) //初始化 relay log 的訪(fǎng)問(wèn)位置 這個(gè)位置比較關(guān)鍵也就是從哪里開(kāi)始讀取我們的relay log。如果出現(xiàn)錯(cuò)誤將會(huì)導(dǎo)致讀取的relay log錯(cuò)誤。 因此我們需要保證rep info的安全,如果設(shè)置了recover relay log 那么將會(huì)初始化為最新一個(gè)relay log的 開(kāi)始位置,因?yàn)樗械奈磮?zhí)行的binlog event將會(huì)從新拉取,老的relay log 已經(jīng)不重要了。后面再說(shuō)。 -> GTID event沒(méi)有辦法使用sql_slave_skip_counter 其具體含義參考: Log_event::do_shall_skip mysql> set global sql_slave_skip_counter=1; ERROR 1858 (HY000): sql_slave_skip_counter can not be set when the server is running with @@GLOBAL.GTID_MODE = ON. Instead, for each transaction that you want to skip, generate an empty transaction with the same GTID as the transaction 進(jìn)入循環(huán) 知道SQL線(xiàn)程被殺死 -> 進(jìn)入狀態(tài)stage_reading_event_from_the_relay_log -> 進(jìn)行一段skip event的判斷和日志輸出 GTID event沒(méi)有辦法使用sql_slave_skip_counter 其具體含義參考: Log_event::do_shall_skip mysql> set global sql_slave_skip_counter=1; ERROR 1858 (HY000): sql_slave_skip_counter can not be set when the server is running with @@GLOBAL.GTID_MODE = ON. Instead, for each transaction that you want to skip, generate an empty transaction with the same GTID as the transaction -> exec_relay_log_event 讀取應(yīng)用 一個(gè)event的上層接口 ->next_event 讀取下一個(gè)Event 完成MTS的檢查點(diǎn) ->獲取開(kāi)始位置 rli->set_event_start_pos(my_b_tell(cur_log)); ->Log_event::read_log_event ->如果是MTS 是否需要進(jìn)行檢查點(diǎn) 1、是否超過(guò)檢查點(diǎn)周期 周期檢查在函數(shù)mts_checkpoint_routine內(nèi)部 set_timespec_nsec(&curr_clock, 0); ulonglong diff= diff_timespec(&curr_clock, &rli->last_clock); if (!force && diff < period) { /* We do not need to execute the checkpoint now because the time elapsed is not enough. */ DBUG_RETURN(FALSE); } 2、是否已經(jīng)GAQ已經(jīng)滿(mǎn)了 bool force= (rli->checkpoint_seqno > (rli->checkpoint_group - 1)); //如果達(dá)到了 GAQ的大小 設(shè)置為force 強(qiáng)制checkpoint ->是否relay log 大小已經(jīng)達(dá)到最大 是否需要relay log切換 但是需要注意如果本事物沒(méi)有結(jié)束不能進(jìn)行切換
/* If we have reached the limit of the relay space and we 如果我們達(dá)到 relay_log_space_limit 上限 需要通知IO THREAD進(jìn)行切換 清理空間``` are going to sleep, waiting for more events: 1. If outside a group, SQL thread asks the IO thread to force a rotation so that the SQL thread purges logs next time it processes an event (thus space is freed). 2. If in a group, SQL thread asks the IO thread to ignore the limit and queues yet one more event so that the SQL thread finishes the group and is are able to rotate and purge sometime soon. */ if (rli->log_space_limit && rli->log_space_limit < rli->log_space_total) { /* force rotation if not in an unfinished group */ if (!rli->is_parallel_exec()) { rli->sql_force_rotate_relay= !rli->is_in_group(); //如果不是一組就需要切換 } else { rli->sql_force_rotate_relay= (rli->mts_group_status != Relay_log_info::MTS_IN_GROUP); } /* ask for one more event */ rli->ignore_log_space_limit= true;//是一組 不能切換 } ``` ->如果讀取了當(dāng)前relay log的全部的relay log event, ->如果是當(dāng)前relay log ->空閑狀態(tài)下等待io 線(xiàn)程的喚醒,如果是MTS還需要定期醒來(lái)進(jìn)行檢查點(diǎn),如下: ``` if (rli->is_parallel_exec() && (opt_mts_checkpoint_period != 0 || DBUG_EVALUATE_IF("check_slave_debug_group", 1, 0))) { int ret= 0; struct timespec waittime; ulonglong period= static_cast(opt_mts_checkpoint_period * 1000000ULL); ulong signal_cnt= rli->relay_log.signal_cnt; mysql_mutex_unlock(log_lock); do { /* At this point the coordinator has no job to delegate to workers. However, workers are executing their assigned jobs and as such the checkpoint routine must be periodically invoked. */ (void) mts_checkpoint_routine(rli, period, false, true/*need_data_lock=true*/); // TODO: ALFRANIO ERROR mysql_mutex_lock(log_lock); if (DBUG_EVALUATE_IF("check_slave_debug_group", 1, 0)) period= 10000000ULL; set_timespec_nsec(&waittime, period); ret= rli->relay_log.wait_for_update_relay_log(thd, &waittime); } while ((ret == ETIMEDOUT || ret == ETIME) /* todo:remove */ && signal_cnt == rli->relay_log.signal_cnt && !thd->killed); } else { rli->relay_log.wait_for_update_relay_log(thd, NULL); //等待relay log 更改的信號(hào) SQL THREAD 會(huì)等待在這里 } ``` -> 如果不是當(dāng)前relay log 那么 SQL線(xiàn)程應(yīng)用或者分發(fā)完成完成后就可以清理了 并且參數(shù)relay_log_purge需要設(shè)置為1 if (rli->relay_log.purge_first_log (rli, rli->get_group_relay_log_pos() == rli->get_event_relay_log_pos() && !strcmp(rli->get_group_relay_log_name(),rli->get_event_relay_log_name())))//做relay log的清理 -> 如果是單SQL現(xiàn)成 獲取event的時(shí)間 這一步 就是獲取計(jì)算延遲的重要因素,但是注意MTS不是在這里實(shí)在檢查點(diǎn)里面 last_master_timestamp ``` rli->last_master_timestamp= ev->common_header->when.tv_sec + //event header 的timestamp (time_t) ev->exec_time; //獲取event的 timestamp作為 計(jì)算last_master_timestamp的基礎(chǔ)數(shù)據(jù) query event才有的執(zhí)行時(shí)間 DBUG_ASSERT(rli->last_master_timestamp >= 0); //但是對(duì)于MTS來(lái)講應(yīng)該注意是最后一個(gè)XID EVENT的 時(shí)間不是這里設(shè)置的 在mts_checkpoint_routine里面 ``` -> 如果GITD_MODE 且AUTO_POSITION 且是MTS需要由協(xié)調(diào)線(xiàn)程進(jìn)行半事物的恢復(fù) (partial transaction) 構(gòu)造回滾EVENT進(jìn)行恢復(fù),而對(duì)已非MTS會(huì)在gtid event做回滾。 這種情況可能出現(xiàn)在: - AUTO_POSITION情況下如果重連,會(huì)重新發(fā)送已經(jīng)傳輸?shù)腅vent。 - AUTO_POSITION情況下如果從庫(kù)異常宕機(jī)重啟,并且recovery_relay_log=0的情況下,會(huì)重新發(fā)送已經(jīng)傳輸?shù)腅vent,并且relay log pos不會(huì)重置 因此我們前面在IO線(xiàn)程和DUMP線(xiàn)程中已經(jīng)討論了,每次sql線(xiàn)程的啟動(dòng)都會(huì)通過(guò)GTID去重新尋找需要拉取的 位置。 coord_handle_partial_binlogged_transaction(rli, ev) -> apply_event_and_update_pos 非MTS 完成 應(yīng)用 MTS完成分發(fā) -> 進(jìn)行skip event操作 -> 維護(hù)skip counter計(jì)數(shù)器 if (reason == Log_event::EVENT_SKIP_COUNT) { --rli->slave_skip_counter;//維護(hù)skip count skip_event= TRUE; } 我們看到slave_skip_counter是以event為單位的,但是對(duì)于最后一個(gè)event如果跨事務(wù)了 那么整個(gè)事物都需要跳過(guò)。但是skip在GTID模式下是不能用的。 -> 如果不能跳過(guò)的事務(wù) 就需要應(yīng)用了。MTS則完成分發(fā) ->完成延遲應(yīng)用邏輯 sql_delay_event(ev, thd, rli) ->ev->apply_event(rli); 這里單SQL線(xiàn)程應(yīng)用 MTS完成分發(fā),分發(fā)方式參考前面 ->是否是進(jìn)行 MTS recovery if (rli->is_mts_recovery()) 根據(jù) bitmap 設(shè)置進(jìn)行跳過(guò)處理 if (rli->is_mts_recovery())//如果是恢復(fù) 這個(gè)地方就是前面恢復(fù)掃描出來(lái)的位置 { bool skip= bitmap_is_set(&rli->recovery_groups, rli->mts_recovery_index) && (get_mts_execution_mode(::server_id, rli->mts_group_status == Relay_log_info::MTS_IN_GROUP, rli->current_mts_submode->get_type() == MTS_PARALLEL_TYPE_DB_NAME) == EVENT_EXEC_PARALLEL); if (skip) { DBUG_RETURN(0); } else { DBUG_RETURN(do_apply_event(rli)); } }
到此,相信大家對(duì)“從庫(kù)的SQL線(xiàn)程和sql_slave_skip_counter參數(shù)分析”有了更深的了解,不妨來(lái)實(shí)際操作一番吧!這里是創(chuàng)新互聯(lián)網(wǎng)站,更多相關(guān)內(nèi)容可以進(jìn)入相關(guān)頻道進(jìn)行查詢(xún),關(guān)注我們,繼續(xù)學(xué)習(xí)!