Explicación detallada de la relación entre la marca de tiempo binlog y exec_time.
Autor: Li Xichao, DBA de Jiangsu Commercial Bank, responsable de la operación, mantenimiento y construcción de bases de datos y middleware. Bueno en MySQL, Python, Oracle y le encanta el ciclismo, la investigación tecnológica y el intercambio.
Producido por la comunidad de código abierto de Aikeson, el contenido original no se puede utilizar sin autorización. Comuníquese con el editor e indique la fuente para la reimpresión.
Este artículo tiene aproximadamente 2000 palabras y se espera que demore 8 minutos en leerlo.
Descripción general
Recientemente, cuando se probó un sistema, se descubrió que había un retraso en la sincronización maestro-esclavo y la causa del retraso se confirmó a través de binlog. Después de usar el comando mysqlbinlog para analizarlo, descubrí que la información que contenía era "algo vaga pero no comprensible".
Por ejemplo, para el siguiente fragmento de 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
Del binlog anterior, podemos saber (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操作
Además (P2):
original_commit_timestamp=2024-04-30 18:38:53
immediate_commit_timestamp=2024-04-30 18:38:59
exec_time=37
Respecto a la información P2, se formulan las siguientes preguntas:
- P1: ¿Qué significan los campos en P2? ¿Cómo se calcula?
- P2: ¿Cuál es la relación entre los campos de P2 y la marca de tiempo vista por P1?
- P4: ¿Cómo se obtiene la marca de tiempo en P1? Especialmente en un entorno amo-esclavo.
Con este fin, a través de la verificación de pruebas y el análisis del código fuente, exec_time
se analiza el origen de los tiempos de eventos comunes y en binlog, y se resume la relación entre los campos.
El siguiente análisis se basa en MySQL 8.0 y los campos pueden ser diferentes en diferentes versiones.
Registro binlog del nodo maestro
1. Evento GTID
marca de tiempo
Para el nodo principal: si no hay instrucciones especiales, el evento debe obtener la última marca de tiempo ( ) timestamp
en la posición inicial de cada ejecución de hilo y asignarla cuando se produce el objeto de evento .dispatch_command()
thd->start_time
thd->start_time
Log_event::common_header->when
La información de la pila principal es la siguiente:
|-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)
marca_hora_commit_inmediata/marca_hora_commit_original
immediate_commit_timestamp
La marca de tiempo obtenida es la hora de envío y el nodo maestro original_commit_timestamp
es igual a 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. COMENZAR Evento
marca de tiempo
Nota: Para el evento BEGIN del nodo maestro, timestamp
no es la marca de tiempo cuando se ejecuta BEGIN, sino la primera operación de modificación. Después de completar la modificación de la primera fila de datos en la capa InnoDB, se genera y escribe el evento Table_map. Antes de generar el evento Table_map, si el caché binlog de toda la transacción está vacío en este momento, la operación se obtendrá inmediatamente thd->start_time
y se generará el evento BEGIN real.
tiempo_ejecutivo
Al mismo tiempo, para el nodo maestro, exec_time
se obtiene obteniendo la última marca de tiempo: BEGIN Event en el proceso de generación de BEGIN Event timestamp
.
tiempo_ejecutivo = A - B
- R: El momento en que se genera el evento BEGIN después de ejecutar el primer SQL modificado y completar la operación de modificación de la primera fila (escribir/actualizar/eliminar).
- B: El tiempo de ejecución inicial del primer SQL modificado (thd->start_time)
La pila interna y la secuencia de ejecución son las siguientes:
3. Evento Table_map
4. Escribir evento
5. Evento Xid
6. Resumen del nodo maestro
- Además del evento BEGIN,
timestamp
es la hora de inicio de la primera operación que debe escribirse en el binlog (como: escribir/actualizar/eliminar); - Para otros Eventos,
timestamp
es la hora de inicio en la que se ejecuta la instrucción SQL; immediate_commit_timestamp/original_commit_timestamp
Esa es la marca de tiempo cuando se envió;- tiempo_ejecutivo = A - B
- R: El momento en que se genera el evento BEGIN después de ejecutar el primer SQL modificado y completar la operación de modificación de la primera fila (escribir/actualizar/eliminar).
- B: El tiempo de ejecución inicial del primer SQL modificado (thd->start_time)
Registro binlog del nodo esclavo
1. Evento GTID
marca de tiempo
En el nodo esclavo: para el evento GTID, MySQL no obtendrá la marca de tiempo del evento GTID/XID del nodo maestro al analizar el evento, por lo que "heredará" la marca de tiempo de la operación anterior de la transacción. Las marcas de tiempo de todas las operaciones de modificación en el nodo esclavo provienen de la marca de tiempo cuando el nodo maestro realiza la operación. Por lo tanto, la hora del evento GTID/XID del nodo esclavo es la marca de tiempo de la última operación de modificación del nodo maestro.
marca_hora_commit_inmediata/marca_hora_commit_original
immediate_commit_timestamp
Obtenga la marca de tiempo de envío del nodo esclavo. Obtenido original_commit_timestamp
del evento GTID original_commit_timestamp
, es decir, el nodo principal envía la operación timestamp
.
La información de la pila principal es la siguiente:
|-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)
oficial
marca_hora_commit_inmediata - marca_hora_commit_original = A + B + C
- A = El tiempo que tarda el nodo maestro en transferir binlog al nodo esclavo
- B = El tiempo que lleva reproducir el binlog desde el nodo esclavo
- C = retardo de sincronización/tiempo de interrupción
2. COMENZAR Evento
marca de tiempo
Aquí timestamp
proviene del evento BEGIN del nodo principal timestamp
. Cuando realmente se ejecute, el evento BEGIN se obtendrá timestamp
y se asignará a thd->start_time/thd->user_time
. Al generar un objeto Evento desde un nodo, simplemente continúe obteniendo thd->start_time
la marca de tiempo de .
tiempo_ejecutivo
Luego, el nodo esclavo exec_time
aún obtiene la última marca de tiempo en el proceso de generar el evento BEGIN timestamp
(tenga en cuenta que timestamp
se obtiene el tiempo de ejecución inicial del SQL modificado del nodo maestro).
La información de la pila principal es la siguiente:
|-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)
...
oficial
tiempo_ejecutivo = A + B + C + D
- A = nodo maestro, todo el tiempo de la transacción
- B = tiempo de transmisión binlog
- C = retraso de sincronización/tiempo de interrupción (probablemente - mayor)
- D = Completar la primera fila de modificación de datos del nodo
original_commit_timestamp: la marca de tiempo = del evento de inicio indica el consumo de tiempo real de toda la transacción en el nodo maestro ([Primera modificación principal] a [Inicio de confirmación principal]).
3. Evento Table_map
4. Escribir evento
5. Evento Xid
6. Desde la sección de nodos
- A excepción del evento GTID/XID, las marcas de tiempo de otros eventos provienen de los eventos del nodo maestro;
- El evento GTID/XID es
timestamp
la hora de inicio de la última operación de modificación del nodo maestro; - El evento GTID
original_commit_timestamp
proviene del nodo maestro yimmediate_commit_timestamp
es la última marca de tiempo; - tiempo_ejecutivo = A - B
- A = La última marca de tiempo del evento BEGIN generado desde el nodo
- B = Hora de inicio del nodo maestro para ejecutar la primera operación DML
Conclusión
Hasta ahora, las marcas de tiempo y la información en el binlog exec_time
se han resuelto básicamente. Los amigos interesados pueden volver al principio del artículo y ver si hay respuestas para las preguntas Q1-Q3.
Finalmente, se recomienda que los lectores simulen varios casos para tener una comprensión más profunda de los campos relevantes, de modo que puedan sentirse más cómodos al usar binlog para analizar problemas de sincronización maestro-esclavo.
La información anterior es solo para comunicación. El nivel del autor es limitado. Si hay alguna deficiencia, no dude en comunicarse en el área de comentarios.
Para obtener más artículos técnicos, visite: https://opensource.actionsky.com/
Acerca de SQLE
SQLE es una plataforma integral de gestión de calidad de SQL que cubre la auditoría y gestión de SQL desde los entornos de desarrollo hasta los de producción. Admite bases de datos nacionales, comerciales y de código abierto convencionales, proporciona capacidades de automatización de procesos para el desarrollo, operación y mantenimiento, mejora la eficiencia en línea y mejora la calidad de los datos.
obtener SQLE
tipo | DIRECCIÓN |
---|---|
Repositorio | https://github.com/actiontech/sqle |
documento | https://actiontech.github.io/sqle-docs/ |
noticias de lanzamiento | https://github.com/actiontech/sqle/releases |
Documentación de desarrollo del complemento de auditoría de datos | https://actiontech.github.io/sqle-docs/docs/dev-manual/plugins/howtouse |