MySQL 同期遅延が発生したときに Seconds_Behind_Master が 0 のままになる理由

MySQL 同期遅延が発生したときに Seconds_Behind_Master が 0 のままになる理由

問題の説明

ユーザーはプライマリ データベースに対して変更操作を実行しましたが、これには約 1 時間かかりました。操作が完了すると、スレーブ データベースは同期遅延があることを検出しますが、監視チャートの Seconds_Behind_Master インジケーターは 0 を示し、binlog の遅延距離は継続的に増加しています。

原理分析

遅延時間を分析しているので、当然、遅延を計算する方法から始めます。便宜上、公式バージョン 5.7.31 のソース コードを引用してここに示します。遅延時間を計算するためのコードを見つけます:

./sql/rpl_slave.cc

ブール show_slave_status_send_data(THD *thd, Master_info *mi,
                                 char* io_gtid_set_buffer、
                                 char*sql_gtid_set_buffer) の引数
......
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())))
    {
      mi->slave_running == MYSQL_SLAVE_RUN_CONNECT の場合
        プロトコル->store(0LL);
      それ以外
        プロトコル->store_null();
    }
    それ以外
    {
      長いtime_diff = ((long)(time(0) - mi->rli->last_master_timestamp)
                       - mi->clock_diff_with_master);

      protocol->store((longlong)(mi->rli->last_master_timestamp ?
                                   最大値(0L、時間差):0));
    }
......

time_diff の計算方法から、この遅延は基本的に時間差であり、その後マスターとスレーブ間の時間差が計算されることがわかります。ただし、if ステートメントが多数あるため、ソース コード ファイル内のコメントを使用します。

  /*
     Seconds_Behind_Master を計算する疑似コード:
     if (SQL スレッドが実行中)
     {
       if (SQL スレッドが利用可能なすべてのリレー ログを処理した場合)
       {
         if (IOスレッドが実行中)
            0 を印刷します。
         それ以外
            NULLを印刷します。
       }
        それ以外
          Seconds_Behind_Masterを計算します。
      }
      それ以外
       NULLを印刷します。
  */

Seconds_Behind_Master の計算は 2 つの部分に分かれていることがわかります。

  • SQL スレッドが正常で、すべてのリレーログが再生され、IO スレッドが正常であれば、直接 0 に設定されます。
  • SQL スレッドが正常で、すべてのリレーログが再生されているときに、IO スレッドが正常でない場合は、直接 NULL を設定します。
  • SQL スレッドが正常で、すべてのリレーログが再生されていない場合、遅延時間が計算されます。

最後に遅延時間を計算するときに、これらの変数の意味を確認します。

  • time(0): タイムスタンプ形式の現在のタイムスタンプ。
  • last_master_timestamp: このイベントがマスター データベースで実行された時刻 (タイムスタンプ形式)。
  • clock_diff_with_master: IO スレッドの開始時に取得されるスレーブとマスター間の時間差。

遅延を計算する際、実際には、マスター上で再生されたイベントが実行された時刻をスレーブのローカル時刻から減算し、両者の時間差を補正することで値が得られることがわかります。論理的には問題はありません。time(0) と clock_diff_with_master がほとんどの場合にエラーを起こす可能性はないので、今回の問題は last_master_timestamp にあるはずです。

PS: ほとんどの場合は問題ありませんが、time(0) はローカル時間を取得します。そのため、スレーブのローカル時間に問題がある場合は、最終的な値も間違ったものになりますが、今回のケースの範囲ではありません。

次に、イベント実行時に last_master_timestamp を計算するロジックを見つけます。コメントと組み合わせると、通常のレプリケーションと並列レプリケーションでは異なる計算方法が使用されていることがわかります。1 つ目は通常のレプリケーションで、計算時点はイベントの実行前です。

./sql/rpl_slave.cc

......
  もし(もし)
  {
    enum_slave_apply_event_and_update_pos_retval exec_res; 列挙型 enum_slave_apply_event_and_update_pos_retval exec_res;

    ptr_ev = &ev; です。
    /*
      このイベントを実行しなくても、マスタータイムスタンプは保持されます。
      マスターの秒数が正しいデルタを示すようにする(イベントがある
      再生されないため、遅れを取り続けます。

      人工的なイベント、またはリレーログイベント(IOスレッドによって生成されたイベント)の場合
      イベント)またはev->whenが0に設定されている場合、またはマスターからのFD、またはハートビート
      server_id が '0' のイベントの場合、last_master_timestamp は更新されません。

      並列実行の場合、last_master_timestampは次の場合にのみ更新されます。
      ジョブはGAQから取り出されます。したがって、last_master_timestampが0(
      GAQが空であることを示し、すべてのスレーブワーカーはイベントを待機しています
      コーディネーターの場合、最初のタイムスタンプで初期化する必要があります
      並行して実行されるイベント。
    */
    if ((!rli->is_parallel_exec() || rli->last_master_timestamp == 0) &&
         !(ev->is_artificial_event() || ev->is_relay_log_event() ||
          (ev->common_header->when.tv_sec == 0) ||
          ev->get_type_code() == binary_log::FORMAT_DESCRIPTION_EVENT ||
          ev->server_id == 0))
    {
      rli->last_master_timestamp = ev->common_header->when.tv_sec +
                                  (time_t)ev->exec_time;
      DBUG_ASSERT(rli->last_master_timestamp >= 0);
    }
......

last_master_timestamp の値は、イベントの開始時刻と実行時刻を足した値です。5.7 では、多くのイベントに実行時刻の値はありませんでした。8.0 では、多くのイベントにこの値が追加されているため、8.0 にアップグレードすることでもたらされたメリットと言えます。

並列レプリケーションの計算方法は次のとおりです。

./sql/rpl\_スレーブ.cc

......
  /*
    この時点でこれが呼び出されないようにする必要があります
    cntはゼロです。この値はチェックポイント情報
    完全にリセットされます。
  */

  /*
    正しい Seconds_behind_master を報告するために rli->last_master_timestamp を更新します。

    GAQ が空の場合はゼロに設定します。
    それ以外の場合は、Slave_job_queueの最初のジョブのタイムスタンプで更新します。
    これは Log_event::get_slave_worker() 関数で割り当てられました。
  */
  ts = rli->gaq->空()
    ? 0
    : reinterpret_cast<Slave_job_group*>(rli->gaq->head_queue())->ts;
  rli->reset_notified_checkpoint(cnt、ts、need_data_lock、true);
  /* "Coordinator::"commit_positions" の終了 */

......

コーディネーターの commit_positions ロジックでは、gaq キューが空の場合、last_master_timestamp は直接 0 に設定され、それ以外の場合は gaq キューの最初のジョブのタイムスタンプが選択されます。この計算はリアルタイムではなく、断続的に行われることを付け加えておきます。計算ロジックの前に、次のロジックがあります。

  /*
    現在、チェックポイント ルーチンは SQL スレッドによって呼び出されています。
    そのため、この関数は適切なポイントから呼び出されます
    SQLスレッドの実行パスで経過時間が計算されます
    ここで、実行するタイミングを確認します。
  */
  set_timespec_nsec(&curr_clock, 0);
  ulonglong diff = diff_timespec(&curr_clock, &rli->last_clock);
  if (!force && diff < ピリオド)
  {
    /*
      チェックポイントを今すぐ実行する必要はありません。
      経過時間が十分ではありません。
    */
    DBUG_RETURN(偽);
  }

つまり、この期間の時間間隔内では、last_master_timestamp は更新されずに直接返されます。そのため、並列レプリケーションでは、Seconds_Behind_Master の値が時々 0 から 1 に変化することが時々あります。

gaq キューの操作はスタックのプッシュおよびポップ操作に似ているため、gaq に残されたトランザクションは常に未完了のトランザクションです。したがって、一般的なシナリオの観点からは、時間計算は問題ありません。

問題分析

原理分析では、全体の計算ロジックを簡単に説明します。それでは、質問自体に戻りましょう。Tencent Cloud Database MySQLでは、デフォルトで並列レプリケーションが有効になっているため、gaqキューが存在し、alter操作に非常に長い時間がかかります。alter操作が並列トランザクションのグループで実行されるかどうかに関係なく(おそらく、DDLは常に別のトランザクショングループです)、gaqキューは最終的に空になり、last_master_timestampは0に設定されます。Seconds_Behind_Masterの計算ロジックを参照すると、最終的なtime_diffも0に設定されるため、alter操作が終了するまでの遅延時間は常に0になります。変更操作が実行されると、gaq キューは新しいイベントとトランザクションでいっぱいになるため、遅延は以前は 0 であったとしても、突然非常に高い値に跳ね上がる可能性があります。

拡大する

通常のレプリケーションと並列レプリケーションの計算方法の違いを比較すると、次の特性がわかります。

  • 並列レプリケーションを有効にすると、遅延時間が 0 と 1 の間を頻繁にジャンプします。
  • 通常のレプリケーションでは発生しませんが、並列レプリケーションのシナリオでは、変更操作や単一の大規模トランザクションによって不正確な遅延が発生する傾向があります。
  • マスター・スレーブ間の時間差はIOスレッド開始時に計算されるため、この期間中にスレーブ時間がずれると遅延時間もずれてしまいます。

総括する

厳密な遅延判断には、GTIDギャップとbinlog位置ギャップに頼る方が良いでしょう。8.0でのイベント実行時間の変化から判断すると、少なくともOracleの担当者はまだ懸命に取り組んでいます。これらの小さな問題ができるだけ早く修正されることを願っています。

上記は、MySQL 同期遅延が発生したときに Seconds_Behind_Master が 0 のままである理由の詳細な内容です。MySQL 同期遅延 Seconds_Behind_Master が 0 である理由の詳細については、123WORDPRESS.COM の他の関連記事に注目してください。

以下もご興味があるかもしれません:
  • MySQLのSeconds_Behind_Masterの詳細な説明
  • Python3 ファイルコピーと遅延ファイルコピータスクの実装方法
  • Docker で MySQL マスター スレーブ レプリケーションを実装するためのサンプル コード
  • MySQL データベース データのロード 複数の用途
  • MySQL データベース シェル import_table データ インポート
  • Mysql データベースのマスタースレーブ同期構成
  • MySQL でシンプルな検索エンジンを実装するためのサンプルコード
  • MySQLコマンドが中国語で入力できない問題の解決方法
  • 面接官がmysqlのcharとvarcharの違いを尋ねたとき
  • MySQL スレーブ ライブラリ Seconds_Behind_Master 遅延の概要

<<:  JS配列メソッドの詳細な説明

>>:  HTMLとリソースがどのように読み込まれるかを理解します

推薦する

TypeScript ジェネリックを簡単に説明する方法

目次概要ジェネリック医薬品とはビルドシステムジェネリック医薬品の一般的な理解ジェネリッククラスジェネ...

Webサービスのリモートデバッグとタイムアウト動作原理の分析

Webサービスのリモートデバッグ.NET では、WEBSERVICE のリモート デバッグ機能はデフ...

MySQLカスタム変数の概念と特徴

MySQL カスタム値は、値を保存するための一時的なコンテナです。サーバーへの接続がアクティブである...

重複リクエストを削除するAxiosのソリューションについての簡単な説明

目次1. 重複したリクエストをキャンセルする2. すべてのリクエストをクリーンアップするこのソリュー...

MySQL スライディング集計/年初来集計の原理と使用例の分析

この記事では、例を使用して、MySQL スライディング集計/年初来集計の原理と使用方法を説明します。...

WINDOWS での MYSQL のインストールに関する詳細なチュートリアル

1. インストールパッケージをダウンロードする- お使いのコンピュータシステムに応じて適切なバージョ...

Vueプラグインの書き方を説明する記事

目次プラグインとはプラグインの作成プラグインの使用要約するプラグインとはVue フレームワークでは、...

CSS3を使用して背景画像の色を変更するさまざまな方法

CSS3 では画像の色を変更できます。これからは複数の絵をデザインする必要がなくなり、いつでも修正で...

Kali に docker と portainer をインストールする方法

dockerの登場により、多くのサービスが徐々にハードウェアアーキテクチャへの依存から脱却しました。...

JavaScript でピンボール ゲームの Web バージョンを実装する

参考までに、JavaScriptのオブジェクトとメソッドを使用して実装されたWebピンボールゲームを...

JavaScript のクロージャによって発生する問題を回避する

閉鎖による問題を回避するためのletについてオブジェクト指向の考え方を使用して、購入者情報の削除機能...

jQueryは動的タグイベントを実装します

この記事では、タグイベントを動的に追加するためのjQueryの具体的なコードを参考までに紹介します。...

MySQL 5.7.18 winx64 のインストールと設定方法のグラフィックチュートリアル

圧縮パッケージのインストールは、mysql-5.7 以降、大幅に変更されました。この記事では、mys...

VueプロジェクトにPWAを導入する手順

目次1. 依存関係をインストールする2. vue.config.js ファイルで pwa を設定しま...

Nginx 仮想ホストを構成する 3 つの方法 (ポートベース) の詳細な説明

Nginx は、IP ベースの仮想ホスト構成、ポート ベースの仮想ホスト構成、ドメイン名ベースの仮想...