溫馨提示×

溫馨提示×

您好,登錄后才能下訂單哦!

密碼登錄×
登錄注冊×
其他方式登錄
點擊 登錄注冊 即表示同意《億速云用戶服務(wù)條款》

MySQL中備庫Seconds_Behind_Master計算的示例分析

發(fā)布時間:2021-11-01 15:07:31 來源:億速云 閱讀:125 作者:小新 欄目:MySQL數(shù)據(jù)庫

這篇文章主要為大家展示了“MySQL中備庫Seconds_Behind_Master計算的示例分析”,內(nèi)容簡而易懂,條理清晰,希望能夠幫助大家解決疑惑,下面讓小編帶領(lǐng)大家一起研究并學習一下“MySQL中備庫Seconds_Behind_Master計算的示例分析”這篇文章吧。

背景

在mysql主備環(huán)境下,主備同步過程如下,主庫更新產(chǎn)生binlog, 備庫io線程拉取主庫binlog生成relay log。備庫sql線程執(zhí)行relay log從而保持和主庫同步。

MySQL中備庫Seconds_Behind_Master計算的示例分析

理論上主庫有更新時,備庫都存在延遲,且延遲時間為備庫執(zhí)行時間+網(wǎng)絡(luò)傳輸時間即t4-t2。

那么mysql是怎么來計算備庫延遲的?

先來看show slave status中的一些信息,io線程拉取主庫binlog的位置:

Master_Log_File: mysql-bin.000001 Read_Master_Log_Pos: 107

sql線程執(zhí)行relay log的位置:

 Relay_Log_File: slave-relay.000003 Relay_Log_Pos: 253

sql線程執(zhí)行的relay log相對于主庫binlog的位置:

Relay_Master_Log_File: mysql-bin.000001 Exec_Master_Log_Pos: 107

源碼實現(xiàn)

Seconds_Behind_Master計算的源碼實現(xiàn)如下:

if ((mi->get_master_log_pos() == mi->rli->get_group_master_log_pos()) &&
    (!strcmp(mi->get_master_log_name(), mi->rli->get_group_master_log_name())))
{ if (mi->slave_running == MYSQL_SLAVE_RUN_CONNECT)
    protocol->store(0LL); else protocol->store_null();
} else {
  long time_diff= ((long)(time(0) - mi->rli->last_master_timestamp)
                   - mi->clock_diff_with_master);

  protocol->store((longlong)(mi->rli->last_master_timestamp ? max(0L, time_diff) : 0));
}

大致可以看出是通過時間和位點來計算的,下面詳細分析下。

if里面條件表示如果io線程拉取主庫binlog的位置和sql線程執(zhí)行的relay log相對于主庫binlog的位置相等,那么認為延遲為0。一般情況下,io線程比sql線程快。但如果網(wǎng)絡(luò)狀況特別差,導致sql線程需等待io線程的情況,那么這兩個位點可能相等,會導致誤認為延遲為0。

再看else里:

  • clock_diff_with_master
    io線程啟動時會向主庫發(fā)送sql語句“SELECT UNIX_TIMESTAMP()”,獲取主庫當前時間,然而用備庫當前時間減去此時間或者主備時間差值即為clock_diff_with_master。這里如果有用戶中途修改了主庫系統(tǒng)時間或修改了timestamp變量,那么計算出備庫延遲時間就是不準確的。

  • last_master_timestamp
    表示主庫執(zhí)行binlog事件的時間。此時間在并行復(fù)制和非并行復(fù)制時的計算方法是不同的

非并行復(fù)制:
備庫sql線程讀取了relay log中的event,event未執(zhí)行之前就會更新last_master_timestamp,這里時間的更新是以event為單位。

rli->last_master_timestamp= ev->when.tv_sec + (time_t) ev->exec_time;

ev->when.tv_sec表示事件的開始時間。exec_time指事件在主庫的執(zhí)行時間,只有Query_log_event和Load_log_event才會統(tǒng)計exec_time。
另外一種情況是sql線程在等待io線程獲取binlog時,會將last_master_timestamp設(shè)為0,按上面的算法Seconds_Behind_Master為0,此時任務(wù)備庫是沒有延遲的。

并行復(fù)制:

并行復(fù)制有一個分發(fā)隊列g(shù)aq,sql線程將binlog事務(wù)讀取到gaq,然后再分發(fā)給worker線程執(zhí)行。并行復(fù)制時,binlog事件是并發(fā)穿插執(zhí)行的,gaq中有一個checkpoint點稱為lwm, lwm之前的binlog都已經(jīng)執(zhí)行,而lwm之后的binlog有些執(zhí)行有些沒有執(zhí)行。
假設(shè)worker線程數(shù)為2,gap有1,2,3,4,5,6,7,8個事務(wù)。worker 1已執(zhí)行的事務(wù)為1 4 6, woker 2執(zhí)行的事務(wù)為2 3 ,那么lwm為4。

并行復(fù)制更新gap checkpiont時,會推進lwm點,同時更新last_master_timestamp為lwm所在事務(wù)結(jié)束的event的時間。因此,并行復(fù)制是在事務(wù)執(zhí)行完成后才更新last_master_timestamp,更新是以事務(wù)為單位。同時更新gap checkpiont還受slave_checkpoint_period參數(shù)的影響。

這導致并行復(fù)制下和非并行復(fù)制統(tǒng)計延遲存在差距,差距可能為slave_checkpoint_period + 事務(wù)在備庫執(zhí)行的時間。這就是為什么在并行復(fù)制下有時候會有很小的延遲,而改為非并行復(fù)制時反而沒有延遲的原因。

另外當sql線程等待io線程時且gaq隊列為空時,會將last_master_timestamp設(shè)為0。同樣此時認為沒有延遲,計算得出seconds_Behind_Master為0。

位點信息維護

  • io線程拉取binlog的位點

    Master_Log_File 讀取到主庫ROTATE_EVENT時會更新(process_io_rotate) Read_Master_Log_Pos:io線程每取到一個event都會從event中讀取pos信息并更新
    mi->set_master_log_pos(mi->get_master_log_pos() + inc_pos);
  • sql線程執(zhí)行relay log的位置

    Relay_Log_File
      sql線程處理ROTATE_EVENT時更新(Rotate_log_event::do_update_pos)
    Relay_Log_Pos:
      非并行復(fù)制時,每個語句執(zhí)行完成更新(stmt_done)
    并行復(fù)制時,事務(wù)完成時更新(Rotate_log_event::do_update_pos/ Xid_log_event::do_apply_event/stmt_done)
  • sql線程執(zhí)行的relay log相對于主庫binlog的位置

    Relay_Master_Log_File
      sql線程處理ROTATE_EVENT時更新(Rotate_log_event::do_update_pos)
    Exec_Master_Log_Pos 和Relay_Log_Pos同時更新
      非并行復(fù)制時,每個語句執(zhí)行完成更新(stmt_done)
      并行復(fù)制時,事務(wù)完成時更新(Rotate_log_event::do_update_pos/ Xid_log_event::do_apply_event/stmt_done)

談到位點更新就有必要說到兩個事件:HEARTBEAT_LOG_EVENT 和 ROTATE_EVENT。

  • HEARTBEAT_LOG_EVENT
    HEARTBEAT_LOG_EVENT我們的了解一般作用是,在主庫沒有更新的時候,每隔master_heartbeat_period時間都發(fā)送此事件保持主庫與備庫的連接。而HEARTBEAT_LOG_EVENT另一個作用是,在gtid模式下,主庫有些gtid備庫已經(jīng)執(zhí)行同時,這些事件雖然不需要再備庫執(zhí)行,但讀取和應(yīng)用binglog的位點還是要推進。因此,這里將這類event轉(zhuǎn)化為HEARTBEAT_LOG_EVENT,由HEARTBEAT_LOG_EVENT幫助我們推進位點。

  • ROTATE_EVENT

    主庫binlog切換產(chǎn)生的ROTATE_EVENT,備庫io線程收到時會也有切換relay log。此rotate也會記入relay log,sql線程執(zhí)行ROTATE_EVENT只更新位點信息。備庫io線程接受主庫的HEARTBEAT_LOG_EVENT,一般不用戶處理。前面提到,gtid模式下,當HEARTBEAT_LOG_EVENT的位點大于當前記錄的位點時,會構(gòu)建一個ROTATE_EVENT,從而讓sql線程推進位點信息。

    if (mi->is_auto_position()  && mi->get_master_log_pos() < hb。log_pos
        &&  mi->get_master_log_name() != NULL)
    {
      mi->set_master_log_pos(hb。log_pos);
      write_ignored_events_info_to_relay_log(mi->info_thd, mi); //構(gòu)建ROTATE_EVENT
      ......
    }

另外,在replicate_same_server_id為0時,備庫接收到的binlog與主庫severid相同時,備庫會忽略此binlog,但位點仍然需要推進。為了效率,此binlog不需要記入relay log。而是替換為ROTATE_EVENT來推進位點。

延遲現(xiàn)象

初始主備是同步的,且沒有任何更新。假設(shè)主備庫執(zhí)行某個DDL在都需要30s,執(zhí)行某個大更新事務(wù)(例如insert..select * from )需要30s。

不考慮網(wǎng)絡(luò)延遲。

MySQL中備庫Seconds_Behind_Master計算的示例分析

  • 非并行復(fù)制時

    執(zhí)行DDL:t2時刻主庫執(zhí)行完,t2時刻備庫執(zhí)行show slave status,Seconds_Behind_Master值為0。同時t2至t3 Seconds_Behind_Master依次增大至30,然后跌0。

    執(zhí)行大事務(wù):t2時刻主庫執(zhí)行完,t2時刻備庫執(zhí)行show slave status,Seconds_Behind_Master值為30。同時t2至t3 Seconds_Behind_Master依次增大至60,然后跌0。

    以上區(qū)別的原因是exec_time只有Query_log_event和Load_log_event才會統(tǒng)計,普通更新沒有統(tǒng)計導致。

  • 并行復(fù)制時

    執(zhí)行DDL:t2時刻主庫執(zhí)行完,t2至t3備庫執(zhí)行show slave status,Seconds_Behind_Master值一直為0

    執(zhí)行大事務(wù):t2時刻主庫執(zhí)行完,t2至t3備庫執(zhí)行show slave status,Seconds_Behind_Master值一直為0

    這是因為執(zhí)行語句之前主備是完全同步的,gaq隊列為空,會將last_master_timestamp設(shè)為0。而執(zhí)行DDL過程中,gap checkpoint一直沒有推進,last_master_timestamp一直未0,直到DDL或大事務(wù)完成。
    所以t2至t3時刻Seconds_Behind_Master值一直為0。而t3時刻有一瞬間last_master_timestamp是會重置的,但又因slave_checkpoint_period會推進checkpoint,gaq隊列變?yōu)榭眨瑫ast_master_timestamp重設(shè)為0。
    因此t3時刻可能看到瞬間有延遲(對于DDL是延遲30s,對于大事務(wù)時延遲60s)。

    這似乎很不合理,gaq隊列為空,會將last_master_timestamp設(shè)為0,這條規(guī)則實際可以去掉。

相關(guān)bug

BUG#72376, PREVIOUS_GTIDS_LOG_EVENT 事件記錄在每個binlog的開頭,表示先前所有文件的gtid集合。relay-log本身event記錄是主庫的時間,但relay log開頭的PREVIOUS_GTIDS_LOG_EVENT事件,是在slave端生成的,時間也是以slave為準的。因此不能用此時間計算last_master_timestamp。修復(fù)方法是在relay log寫PREVIOUS_GTIDS_LOG_EVENT事件是標記是relay log產(chǎn)生的,在統(tǒng)計last_master_timestamp時,發(fā)現(xiàn)是relay產(chǎn)生的事件則忽略統(tǒng)計。

if (is_relay_log)
  prev_gtids_ev。set_relay_log_event();
   ...... if (!(ev->is_artificial_event()||...))
   rli->last_master_timestamp= ev->when。tv_sec + (time_t) ev->exec_time;

以上是“MySQL中備庫Seconds_Behind_Master計算的示例分析”這篇文章的所有內(nèi)容,感謝各位的閱讀!相信大家都有了一定的了解,希望分享的內(nèi)容對大家有所幫助,如果還想學習更多知識,歡迎關(guān)注億速云行業(yè)資訊頻道!

向AI問一下細節(jié)

免責聲明:本站發(fā)布的內(nèi)容(圖片、視頻和文字)以原創(chuàng)、轉(zhuǎn)載和分享為主,文章觀點不代表本網(wǎng)站立場,如果涉及侵權(quán)請聯(lián)系站長郵箱:is@yisu.com進行舉報,并提供相關(guān)證據(jù),一經(jīng)查實,將立刻刪除涉嫌侵權(quán)內(nèi)容。

AI