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とリソースがどのように読み込まれるかを理解します

推薦する

Docker イメージのダウンロードが遅すぎる場合の解決策

Docker イメージのダウンロードが停止したり、遅すぎたりするネットでいろいろな方法を検索しました...

MySQL 文字列分割操作 (区切り文字を含む文字列のインターセプション)

区切り文字なしの文字列抽出質問の要件データベース内のフィールド値:実装効果: 1行のデータを複数行に...

jsはシンプルなショッピングカートモジュールを実装します

この記事の例では、参考までに、シンプルなショッピングカートモジュールを実装するためのjsの具体的なコ...

ES6 における Object.assign() の使い方の詳細な説明

目次2. 目的2.1 オブジェクトにプロパティを追加する2.3 オブジェクトの複製2.4 複数のオブ...

nacos が mysql に接続できない場合の解決策

理由nacos の pom が依存する mysql バージョンが、mysql バージョンと一致してい...

Docker ベースの Redis クラスターの構築方法

Redisイメージをダウンロードする docker pull yyyyttttwww/redis を...

JSが5つ星の賞賛を獲得

この記事では、5つ星の評価を獲得するためのJSの具体的なコードを参考までに共有します。具体的な内容は...

MySQL が UNION を使用して 2 つのクエリを接続できない理由の詳細な説明

概要連合接続データセットキーワードは、2つのクエリ結果セットを1つに連結し、同一のレコードを除外する...

PDO を使用して SQL インジェクションを防ぐ原理の分析

序文この記事では、SQL インジェクションを回避するために pdo の前処理メソッドを使用します。詳...

CentOS SVN サーバーで複数のプロジェクトを管理する方法

一つの要求一般的に、企業には複数のプロジェクトがあります。SVN サーバーを設定した後は、プロジェク...

MySQL 学習: データベース テーブルの 5 つの主要な制約を初心者向けに詳しく説明します

目次1. 制約の概念と分類2. 5つの制約の追加と削除2.1 制約を追加する6つの方法2.2 制約を...

JavaScript における BOM と DOM の詳細な説明

目次BOM (ブラウザ オブジェクト モデル) 1. ウィンドウブラウザのウィンドウサイズを取得する...

Vue バックグラウンドでステータス ラベルをエレガントに記述する例

目次序文最適化変数の抽出二次包装 el-tag コンポーネント使用要約する序文バックエンドシステムの...

CSS のマージンの崩壊問題を解決する方法

まず、マージン崩壊が発生する 3 つの状況を見てみましょう。 1. 隣接する 2 つのブロックレベル...

Vue 監視属性のグラフィック例の詳細な説明

目次リスナープロパティとは何ですか?リスニングプロパティと計算プロパティの違いは何ですか?監視プロパ...