binlog のタイムスタンプと exec_time の関係の詳細な説明。
著者: Li Xichao 氏、江蘇商業銀行 DBA、データベースとミドルウェアの運用、保守、構築を担当。 MySQL、Python、Oracle が得意で、サイクリング、テクノロジーの研究、共有が大好きです。
Aikeson オープンソース コミュニティによって作成されているため、オリジナルのコンテンツを許可なく使用することはできません。転載する場合は編集者に連絡し、出典を明記してください。
この記事は約 2,000 ワードで、読むのに 8 分かかると予想されます。
概要
最近、システムのテストを行ったところ、マスターとスレーブの同期に遅延があることが判明し、binlog により遅延の原因が確認されました。mysqlbinlogコマンドを使用してこれを解析したところ、その情報は「やや曖昧だが理解できない」ことがわかりました。
たとえば、次の binlog スニペットの場合:
# at 449880
#240430 18:38:49 server id 345 end_log_pos 449967 CRC32 0xb3e8a02a GTID last_committed=13 sequence_number=14 rbr_only=yes original_committed_timestamp=1714473533138376 immediate_commit_timestamp=1714473539246294 transaction_length=446792
/*!50718 SET TRANSACTION ISOLATION LEVEL READ COMMITTED*//*!*/;
# original_commit_timestamp=1714473533138376 (2024-04-30 18:38:53.138376 CST)
# immediate_commit_timestamp=1714473539246294 (2024-04-30 18:38:59.246294 CST)
/*!80001 SET @@session.original_commit_timestamp=1714473533138376*//*!*/;
/*!80014 SET @@session.original_server_version=80027*//*!*/;
/*!80014 SET @@session.immediate_server_version=80027*//*!*/;
SET @@SESSION.GTID_NEXT= 'c0ac4587-6046-11ee-9fa7-001c42c92a7b:44'/*!*/;
# at 449967
#240430 18:38:16 server id 345 end_log_pos 450039 CRC32 0x0c7cb74e Query thread_id=16 exec_time=37 error_code=0
SET TIMESTAMP=1714473496/*!*/;
BEGIN
/*!*/;
/*!*/;
# at 450039
#240430 18:38:16 server id 345 end_log_pos 450098 CRC32 0xf9a84808 Table_map: `testdb`.`tb3` mapped to number 110
# at 450098
#240430 18:38:16 server id 345 end_log_pos 458309 CRC32 0xad84e9b0 Write_rows: table id 110
...
# at 896439
#240430 18:38:46 server id 345 end_log_pos 896498 CRC32 0x5cd7cd3b Table_map: `testdb`.`tb3` mapped to number 110
# at 896498
#240430 18:38:46 server id 345 end_log_pos 896540 CRC32 0x21b77031 Write_rows: table id 110 flags: STMT_END_F
...
### INSERT INTO `testdb`.`tb3`
### SET
### @1=131060 /* INT meta=0 nullable=0 is_null=0 */
### @2='c' /* VARSTRING(80) meta=80 nullable=1 is_null=0 */
# at 896540
#240430 18:38:49 server id 345 end_log_pos 896599 CRC32 0x6d6bf911 Table_map: `testdb`.`tb3` mapped to number 110
# at 896599
#240430 18:38:49 server id 345 end_log_pos 896641 CRC32 0xccd2fbb1 Write_rows: table id 110 flags: STMT_END_F
...
### INSERT INTO `testdb`.`tb3`
### SET
### @1=131061 /* INT meta=0 nullable=0 is_null=0 */
### @2='c' /* VARSTRING(80) meta=80 nullable=1 is_null=0 */
# at 896641
#240430 18:38:49 server id 345 end_log_pos 896672 CRC32 0xadb14b9d Xid = 85
上記のバイナリログから、(P1) がわかります。
#240430 18:38:16 执行 begin 开启了事务 (为便于表述,将时间字段名为timestamp)
#240430 18:38:16 执行了 tb3的insert 操作
#240430 18:38:46 执行了 tb3的insert 操作
#240430 18:38:49 执行了 tb3的insert 操作
#240430 18:38:49 执行了commit操作
さらに (P2):
original_commit_timestamp=2024-04-30 18:38:53
immediate_commit_timestamp=2024-04-30 18:38:59
exec_time=37
P2 情報に関して、次の質問が行われます。
- Q1: P2 のフィールドは何を意味しますか?どのように計算されますか?
- Q2: P2 のフィールドと P1 から見えるタイムスタンプとの間にはどのような関係がありますか?
- Q4: P1 のタイムスタンプはどのように取得されますか?特にマスタースレーブ環境では
このために、テスト検証とソースコード分析を通じて、exec_time
共通のイベント時間とバイナリログの起源が分析され、フィールド間の関係が要約されます。
次の分析は MySQL 8.0 に基づいており、フィールドはバージョンによって異なる場合があります。
マスターノードのbinlogログ
1.GTIDイベント
タイムスタンプ
メインノードの場合: 特別な指示がない場合、Event はtimestamp
各スレッドの実行開始位置でdispatch_command()
最新のタイムスタンプ()を取得し、 thd->start_time
Event オブジェクト生成時thd->start_time
に代入しますLog_event::common_header->when
。
主なスタック情報は次のとおりです。
|-handle_connection (./sql/conn_handler/connection_handler_per_thread.cc:302)
|-do_command (./sql/sql_parse.cc:1343)
|-dispatch_command (./sql/sql_parse.cc:1922)
// 设置 thd->start_time
|-thd->set_time()
|-my_micro_time_to_timeval(start_utime, &start_time)
|-dispatch_sql_command (./sql/sql_parse.cc:5135)
|-mysql_execute_command (./sql/sql_parse.cc:3518)
|-Sql_cmd_dml::execute (./sql/sql_select.cc:579)
……
|-Table_map_log_event the_event(this, table, table->s->table_map_id,is_transactional)
……
|-Rows_log_event *const ev = new RowsEventT(this, table, table->s->table_map_id, )
……
|-Xid_log_event end_evt(thd, xid)
即時コミットタイムスタンプ/オリジナルコミットタイムスタンプ
immediate_commit_timestamp
取得されたタイムスタンプは送信時刻であり、マスター ノードはoriginal_commit_timestamp
に等しいですimmediate_commit_timestamp
。
|-error = trx_cache.flush(thd, &trx_bytes, wrote_xid)
|-Transaction_ctx *trn_ctx = thd->get_transaction()
|-trn_ctx->sequence_number = mysql_bin_log.m_dependency_tracker.step()
|-if (trn_ctx->last_committed == SEQ_UNINIT): trn_ctx->last_committed = trn_ctx->sequence_number - 1
|-if (!error): if ((error = mysql_bin_log.write_transaction(thd, this, &writer)))
|-int64 sequence_number, last_committed
|-m_dependency_tracker.get_dependency(thd, sequence_number, last_committed)
|-thd->get_transaction()->last_committed = SEQ_UNINIT
|-ulonglong immediate_commit_timestamp = my_micro_time()
//|-ulonglong original_commit_timestamp = thd->variables.original_commit_timestamp
|-ulonglong original_commit_timestamp = immediate_commit_timestamp
|-uint32_t trx_immediate_server_version = do_server_version_int(::server_version)
|-Gtid_log_event gtid_event(thd, cache_data->is_trx_cache(), last_committed, sequence_number,
cache_data->may_have_sbr_stmts(), original_commit_timestamp,
immediate_commit_timestamp, trx_original_server_version,
trx_immediate_server_version)
2.BEGINイベント
タイムスタンプ
注: マスター ノードの BEGIN イベントの場合、timestamp
これは BEGIN が実行されたときのタイムスタンプではなく、最初の変更操作のタイムスタンプです。 InnoDB レイヤーのデータの最初の行の変更が完了すると、Table_map イベントが生成され、書き込まれます。 Table_map イベントを生成する前に、この時点でトランザクション全体の binlog キャッシュが空であれば、操作はすぐに取得されthd->start_time
、実際の BEGIN イベントが生成されます。
実行時間
同時に、マスターノードの場合は、exec_time
BEGIN Event の生成過程で最新のタイムスタンプ - BEGIN Event を取得することで取得されますtimestamp
。
実行時間 = A - B
- A: 最初に変更された SQL を実行し、最初の行変更 (書き込み/更新/削除) 操作が完了した後、BEGIN イベントが生成された時刻です。
- B: 最初に変更されたSQLの実行開始時刻(thd->start_time)
内部スタックと実行シーケンスは次のとおりです。
3. Table_map イベント
4.書き込みイベント
5.Xidイベント
6. マスターノードの概要
- BEGIN イベントに加えて、
timestamp
バイナリログに書き込む必要がある最初の操作 (書き込み/更新/削除など) の開始時刻です。 - 他のイベントの場合は、
timestamp
SQL ステートメントが実行される開始時刻です。 immediate_commit_timestamp/original_commit_timestamp
これは送信時のタイムスタンプです。- 実行時間 = A - B
- A: 最初に変更された SQL を実行し、最初の行変更 (書き込み/更新/削除) 操作が完了した後、BEGIN イベントが生成された時刻です。
- B: 最初に変更されたSQLの実行開始時刻(thd->start_time)
スレーブノードのbinlogログ
1.GTIDイベント
タイムスタンプ
スレーブ ノード: GTID イベントの場合、MySQL はイベントの解析時にマスター ノードの GTID/XID イベントのタイムスタンプを取得しないため、トランザクションの前の操作のタイムスタンプを「継承」します。スレーブ ノード上のすべての変更操作のタイムスタンプは、マスター ノードが操作を実行したときのタイムスタンプから取得されます。したがって、スレーブ ノードの GTID/XID イベントの時刻は、マスター ノードの最後の変更操作のタイムスタンプになります。
即時コミットタイムスタンプ/オリジナルコミットタイムスタンプ
immediate_commit_timestamp
スレーブ ノードの送信時刻のタイムスタンプを取得します。original_commit_timestamp
GTID Event から取得されますoriginal_commit_timestamp
。つまり、メイン ノードが操作を送信しますtimestamp
。
主なスタック情報は次のとおりです。
|-handle_slave_worker (./sql/rpl_replica.cc:5891)
|-slave_worker_exec_job_group (./sql/rpl_rli_pdb.cc:2549)
|-Slave_worker::slave_worker_exec_event (./sql/rpl_rli_pdb.cc:1760)
|-Xid_apply_log_event::do_apply_event_worker (./sql/log_event.cc:6179)
|-Xid_log_event::do_commit (./sql/log_event.cc:6084)
|-trans_commit (./sql/transaction.cc:246)
|-ha_commit_trans (./sql/handler.cc:1765)
|-MYSQL_BIN_LOG::commit (./sql/binlog.cc:8170)
|-MYSQL_BIN_LOG::ordered_commit (./sql/binlog.cc:8789)
|-MYSQL_BIN_LOG::process_flush_stage_queue (./sql/binlog.cc:8326)
|-MYSQL_BIN_LOG::flush_thread_caches (./sql/binlog.cc:8218)
|-binlog_cache_mngr::flush (./sql/binlog.cc:1099)
|-binlog_cache_data::flush (./sql/binlog.cc:2098)
|-MYSQL_BIN_LOG::write_transaction (./sql/binlog.cc:1586)
// 生成并写入 GTID event
|-ulonglong immediate_commit_timestamp = my_micro_time()
|-if (original_commit_timestamp == UNDEFINED_COMMIT_TIMESTAMP){...}
|-Gtid_log_event gtid_event(thd, cache_data->is_trx_cache(), last_committed, sequence_number,
cache_data->may_have_sbr_stmts(), original_commit_timestamp, immediate_commit_timestamp, trx_original_server_version,
trx_immediate_server_version)
公式
即時コミットタイムスタンプ - オリジナルコミットタイムスタンプ = A + B + C
- A =マスターノードがバイナリログをスレーブノードに転送するのにかかる時間
- B =スレーブ ノードからバイナリログを再生するのにかかる時間
- C = 同期遅延/中断時間
2.BEGINイベント
タイムスタンプ
これはtimestamp
メイン ノードの BEGIN イベントからのものですtimestamp
。実際に実行するとBEGINイベントが取得されtimestamp
、 に代入されますthd->start_time/thd->user_time
。ノードから Event オブジェクトを生成するときは、thd->start_time
からタイムスタンプを取得し続けるだけです。
実行時間
その後、スレーブ ノードはexec_time
、BEGIN イベントの生成プロセスで最新のタイムスタンプtimestamp
(timestamp
マスター ノードからの変更された SQL の実行開始時刻に注意してください) を取得します。
主なスタック情報は次のとおりです。
|-handle_slave_worker (./sql/rpl_replica.cc:5891)
|-slave_worker_exec_job_group (./sql/rpl_rli_pdb.cc:2549)
|-Slave_worker::slave_worker_exec_event (./sql/rpl_rli_pdb.cc:1760)
|-Log_event::do_apply_event_worker (./sql/log_event.cc:1083)
|-Query_log_event::do_apply_event (./sql/log_event.cc:4443)
|-Query_log_event::do_apply_event (./sql/log_event.cc:4606)
// 设置 user_time=start_time=ev.common_header->when
|-thd->set_time(&(common_header->when))
// query_arg="BEGIN"
|-thd->set_query(query_arg, q_len_arg)
...
公式
実行時間 = A + B + C + D
- A =マスターノード、トランザクション時間全体
- B = バイナリログ送信時間
- C = 同期遅延/中断時間 (おそらく - メジャー)
- D = ノードからのデータ変更の最初の行を完了します
original_commit_timestamp - begin イベントの timestamp = は、マスター ノード上のトランザクション全体 ([メイン優先変更] から [メインコミット開始]) の実際の消費時間を示します。
3. Table_map イベント
4.書き込みイベント
5.Xidイベント
6. ノードセクションから
- GTID/XID イベントを除き、他のイベントのタイムスタンプはマスター ノードのイベントから取得されます。
- GTID/XID イベントは、
timestamp
マスター ノードの最後の変更操作の開始時刻です。 - GTID イベントは
original_commit_timestamp
マスター ノードから取得され、immediate_commit_timestamp
最新のタイムスタンプです。 - 実行時間 = A - B
- A =ノードから生成された BEGIN イベントの最新のタイムスタンプ
- B =マスターノードが最初の DML 操作を実行する時間を開始します
結論
この時点で、binlog 内のタイムスタンプと情報はexec_time
基本的に整理されています。興味のある方は記事の先頭に戻って、Q1 ~ Q3 への回答があるかどうかを確認してください。
最後に、binlog を使用してマスター/スレーブ同期の問題をより快適に分析できるように、関連フィールドをより深く理解するためにいくつかのケースをシミュレーションすることをお勧めします。
上記の情報は、作者のレベルに限りがありますので、不足がある場合は、お気軽にコメント欄でご連絡ください。
さらに技術的な記事については、https: //opensource.actionsky.com/をご覧ください。
SQLEについて
SQLE は、開発環境から運用環境までの SQL 監査と管理をカバーする包括的な SQL 品質管理プラットフォームです。主流のオープンソース、商用および国内データベースをサポートし、開発、運用および保守のためのプロセス自動化機能を提供し、オンライン効率を向上させ、データ品質を向上させます。
SQL取得
タイプ | 住所 |
---|---|
リポジトリ | https://github.com/actiontech/sqle |
書類 | https://actiontech.github.io/sqle-docs/ |
リリースニュース | https://github.com/actiontech/sqle/releases |
データ監査プラグイン開発ドキュメント | https://actiontech.github.io/sqle-docs/docs/dev-manual/plugins/howtouse |