Explicación detallada de la relación entre la marca de tiempo binlog de MySQL y exec_time

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_timese 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 ( ) timestampen la posición inicial de cada ejecución de hilo y asignarla cuando se produce el objeto de evento .dispatch_command()thd->start_timethd->start_timeLog_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_timestampLa marca de tiempo obtenida es la hora de envío y el nodo maestro original_commit_timestampes 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, timestampno 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_timey se generará el evento BEGIN real.

tiempo_ejecutivo

Al mismo tiempo, para el nodo maestro, exec_timese 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, timestampes la hora de inicio de la primera operación que debe escribirse en el binlog (como: escribir/actualizar/eliminar);
  • Para otros Eventos, timestampes la hora de inicio en la que se ejecuta la instrucción SQL;
  • immediate_commit_timestamp/original_commit_timestampEsa 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_timestampObtenga la marca de tiempo de envío del nodo esclavo. Obtenido original_commit_timestampdel 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í timestampproviene del evento BEGIN del nodo principal timestamp. Cuando realmente se ejecute, el evento BEGIN se obtendrá timestampy se asignará a thd->start_time/thd->user_time. Al generar un objeto Evento desde un nodo, simplemente continúe obteniendo thd->start_timela marca de tiempo de .

tiempo_ejecutivo

Luego, el nodo esclavo exec_timeaún obtiene la última marca de tiempo en el proceso de generar el evento BEGIN timestamp(tenga en cuenta que timestampse 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 timestampla hora de inicio de la última operación de modificación del nodo maestro;
  • El evento GTID original_commit_timestampproviene del nodo maestro y immediate_commit_timestampes 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_timese 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
¿Cuántos ingresos puede generar un proyecto desconocido de código abierto? El equipo chino de inteligencia artificial de Microsoft empacó colectivamente y se fue a los Estados Unidos, involucrando a cientos de personas. Huawei anunció oficialmente que los cambios de trabajo de Yu Chengdong estaban clavados en el "Pilar de la vergüenza de FFmpeg" durante 15 años. Hace, pero hoy tiene que agradecernos—— ¿Tencent QQ Video venga su humillación pasada? El sitio espejo de código abierto de la Universidad de Ciencia y Tecnología de Huazhong está oficialmente abierto para acceso externo : Django sigue siendo la primera opción para el 74% de los desarrolladores. El editor Zed ha logrado avances en el soporte de Linux. Un ex empleado de una conocida empresa de código abierto . dio la noticia: después de ser desafiada por un subordinado, la líder técnica se puso furiosa y grosera, fue despedida y quedó embarazada. La empleada Alibaba Cloud lanza oficialmente Tongyi Qianwen 2.5 Microsoft dona 1 millón de dólares a la Fundación Rust.
{{o.nombre}}
{{m.nombre}}

Supongo que te gusta

Origin my.oschina.net/actiontechoss/blog/11112538
Recomendado
Clasificación