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

推薦する

CentOS7にMySQL 8.0.26をインストールする手順

1. まず、お使いのマシンに応じて、MySQL 公式サイトから対応するデータベースをダウンロードしま...

Linux 面接で最もよく聞かれる 10 の質問のまとめ

序文Linux システムの運用および保守エンジニア職の面接を受ける場合は、次の 10 個の最も一般的...

Alibaba Cloud Ubuntu 16.04 が IPSec サービスを構築

IPSec の概要IPSec (インターネット プロトコル セキュリティ): ネットワーク層と適用さ...

Redhat 7.3 に MySQL 8.0.22 をインストールするための詳細なチュートリアル (バイナリ インストール)

目次1. MySQLインストールパッケージをダウンロードする2. MySQLのアンインストール手順3...

MySQL の列から行への変換のヒント (共有)

序文:多くのビジネス テーブルでは、歴史的またはパフォーマンス上の理由により、最初のパラダイムに違反...

VMware 仮想マシンでの CentOS7 ネットワーク構成 (ホストのワイヤレス インターネット アクセス)

CentOS7 システムを使用するのは今回が初めてで、ネットワーク構成を行う際に多くの問題が発生し...

CenOS6.7 mysql 8.0.22 のインストールと設定方法のグラフィックチュートリアル

CenOS6.7 は MySQL8.0.22 (推奨コレクション) をインストールします1. MyS...

jQueryはhide()とtoggle()関数を使用してカメラブランド表示の非表示機能を実現します。

最近、jQuery を学習しているときに、show()、hide()、toggle() 関数に出会い...

JS ループで async と await を正しく使用する方法

目次概要(ループモード - 共通)配列と非同期メソッドを宣言して反復するforループで使用するマップ...

Nginx 仮想ホストを構成する 3 つの方法 (ドメイン名に基づく)

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

データベースSQL文の最適化

最適化する理由:実際のプロジェクトが開始され、データベースが一定期間稼働した後、初期のデータベース設...

CSS でショートカット プロパティを記述する際は、トラブルの順序に注意してください (落とし穴を避けるため)

ショートハンドプロパティは、複数のプロパティに同時に値を割り当てるために使用されます。たとえば、fo...

IE6/7 における a.getAttribute(href,2) 問題の分析と解決

簡単な説明<br />IE6および7では、一般的なaタグ(HTMLで記述され、DOM操作...

TypeScript デコレータ定義

目次1. コンセプト1.1 定義1.2 デコレータファクトリー1.3 デコレータの組み合わせ1.4 ...

2 つの MySQL ユーザー削除ステートメント (delete user と drop user) の違い

ヒント: MySQL では、ユーザーの作成と削除が頻繁に必要になります。ユーザーを作成するときは、通...