Explication détaillée de la relation entre l'horodatage binlog et exec_time.
Auteur : Li Xichao, DBA de la Jiangsu Commercial Bank, responsable de l'exploitation, de la maintenance et de la construction des bases de données et des middlewares. Bon en MySQL, Python, Oracle et aime le cyclisme, la recherche technologique et le partage.
Produit par la communauté open source Aikeson, le contenu original ne peut être utilisé sans autorisation. Veuillez contacter l'éditeur et indiquer la source de réimpression.
Cet article compte environ 2 000 mots et sa lecture devrait prendre 8 minutes.
Aperçu
Récemment, lorsqu'un système a été testé, il a été découvert qu'il y avait un retard dans la synchronisation maître-esclave, et la cause du retard a été confirmée via binlog. Après avoir utilisé la commande mysqlbinlog pour l'analyser, j'ai trouvé que les informations qu'il contenait étaient "un peu vagues mais pas compréhensibles".
Par exemple, pour l'extrait de journal binaire suivant :
# 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
À partir du binlog ci-dessus, nous pouvons savoir (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操作
De plus (P2) :
original_commit_timestamp=2024-04-30 18:38:53
immediate_commit_timestamp=2024-04-30 18:38:59
exec_time=37
Concernant les informations P2, les questions suivantes sont posées :
- Q1 : Que signifient les champs de P2 ? Comment est-il calculé ?
- Q2 : Quelle est la relation entre les champs de P2 et l’horodatage vu par P1 ?
- Q4 : Comment l'horodatage de P1 est-il obtenu ? Surtout dans un environnement maître-esclave
À cette fin, grâce à la vérification des tests et à l'analyse du code source, exec_time
l'origine des heures d'événements communes et du binlog est analysée et la relation entre les champs est résumée.
L'analyse suivante est basée sur MySQL 8.0 et les champs peuvent être différents selon les versions.
Journal binlog du nœud maître
1. Événement GTID
horodatage
Pour le nœud principal : s'il n'y a pas d'instructions spéciales, l'événement doit obtenir le dernier horodatage ( ) timestamp
à la position initiale de chaque exécution de thread et l'attribuer au moment où l'objet Event est produit .dispatch_command()
thd->start_time
thd->start_time
Log_event::common_header->when
Les principales informations sur la pile sont les suivantes :
|-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)
immédiat_commit_timestamp/original_commit_timestamp
immediate_commit_timestamp
L'horodatage obtenu est l'heure de soumission et le nœud maître original_commit_timestamp
est égal à 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. COMMENCER l'événement
horodatage
Remarque : Pour l'événement BEGIN du nœud maître, timestamp
il ne s'agit pas de l'horodatage de l'exécution de BEGIN, mais de la première opération de modification. Après avoir terminé la modification de la première ligne de données de la couche InnoDB, l'événement Table_map est généré et écrit. Avant de générer l'événement Table_map, si le cache binlog de toute la transaction est vide à ce moment, l'opération sera obtenue immédiatement thd->start_time
et le véritable événement BEGIN sera généré.
heure_exec
En même temps, pour le nœud maître, exec_time
il est obtenu en obtenant le dernier horodatage - BEGIN Event en cours de génération de BEGIN Event timestamp
.
exec_time = A - B
- R : L'heure à laquelle l'événement BEGIN est généré après l'exécution du premier SQL modifié et la fin de l'opération de modification de la première ligne (écriture/mise à jour/suppression).
- B : L'heure de début d'exécution du premier SQL modifié (thd->start_time)
La pile interne et la séquence d'exécution sont les suivantes :
3. Événement Table_map
4. Écrire un événement
5. Événement XID
6. Résumé du nœud maître
- En plus de l'événement BEGIN,
timestamp
c'est l'heure de début de la première opération qui doit être écrite dans le journal binaire (telle que : écriture/mise à jour/suppression) ; - Pour les autres événements,
timestamp
il s'agit de l'heure de début à laquelle l'instruction SQL est exécutée ; immediate_commit_timestamp/original_commit_timestamp
Il s'agit de l'horodatage lors de la soumission ;- exec_time = A - B
- R : L'heure à laquelle l'événement BEGIN est généré après l'exécution du premier SQL modifié et la fin de l'opération de modification de la première ligne (écriture/mise à jour/suppression).
- B : L'heure de début d'exécution du premier SQL modifié (thd->start_time)
Journal binaire du nœud esclave
1. Événement GTID
horodatage
Sur le nœud esclave : pour l'événement GTID, MySQL n'obtiendra pas l'horodatage de l'événement GTID/XID du nœud maître lors de l'analyse de l'événement, il "héritera donc" de l'horodatage de l'opération précédente de la transaction. Les horodatages de toutes les opérations de modification sur le nœud esclave proviennent de l'horodatage lorsque le nœud maître effectue l'opération. Par conséquent, l’heure de l’événement GTID/XID du nœud esclave est l’horodatage de la dernière opération de modification du nœud maître.
immédiat_commit_timestamp/original_commit_timestamp
immediate_commit_timestamp
Obtenez l'horodatage de l'heure de soumission du nœud esclave. Obtenu original_commit_timestamp
à partir de GTID Événement original_commit_timestamp
,C'est - à - dire que le noeud principal soumet l'opération timestamp
.
Les principales informations sur la pile sont les suivantes :
|-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)
officiel
immédiat_commit_timestamp - original_commit_timestamp = A + B + C
- A = Le temps nécessaire au nœud maître pour transférer le binlog vers le nœud esclave
- B = Le temps nécessaire pour relire le binlog à partir du nœud esclave
- C = délai de synchronisation/temps d'interruption
2. COMMENCER l'événement
horodatage
Cela timestamp
vient de BEGIN Événement du noeud principal timestamp
. Lorsqu'il est effectivement exécuté, l'événement BEGIN sera obtenu timestamp
et attribué à thd->start_time/thd->user_time
. Lors de la génération d'un objet Event à partir d'un nœud, continuez simplement à thd->start_time
obtenir l'horodatage de .
heure_exec
Ensuite, le nœud esclave exec_time
obtient toujours le dernier horodatage dans le processus de génération de l'événement BEGIN timestamp
(notez que timestamp
l'heure de début d'exécution du SQL modifié à partir du nœud maître) est obtenue.
Les principales informations sur la pile sont les suivantes :
|-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)
...
officiel
exec_time = A + B + C + D
- A = nœud maître, toute la durée de la transaction
- B = temps de transmission du binlog
- C = délai de synchronisation/temps d'interruption (probablement - majeur)
- D = Compléter la première ligne de modification des données à partir du nœud
original_commit_timestamp - timestamp = of begin événement indique la consommation de temps réelle de l'ensemble de la transaction sur le nœud maître ([Main-first modification] à [Main-commit start]).
3. Événement Table_map
4. Écrire un événement
5. Événement XID
6. Depuis la section nœud
- À l'exception de l'événement GTID/XID, les horodatages des autres événements proviennent des événements du nœud maître ;
- L'événement GTID/XID est
timestamp
l'heure de début de la dernière opération de modification du nœud maître ; - L'événement GTID
original_commit_timestamp
provient du nœud maître etimmediate_commit_timestamp
constitue le dernier horodatage ; - exec_time = A - B
- A = Le dernier horodatage de l'événement BEGIN généré à partir du nœud
- B = Le nœud maître démarre le temps d'exécution de la première opération DML
Conclusion
À ce stade, les horodatages et les informations du binlog exec_time
ont été essentiellement triés. Les amis intéressés peuvent revenir au début de l'article et voir s'il y a des réponses aux Q1-Q3.
Enfin, il est recommandé aux lecteurs de simuler plusieurs cas afin d'avoir une compréhension plus approfondie des domaines concernés, afin d'être plus à l'aise lors de l'utilisation de binlog pour analyser des problèmes de synchronisation maître-esclave.
Les informations ci-dessus sont uniquement destinées à la communication. Le niveau de l'auteur est limité. S'il y a des lacunes, n'hésitez pas à communiquer dans la zone de commentaires.
Pour des articles plus techniques, veuillez visiter : https://opensource.actionsky.com/
À propos de SQLE
SQLE est une plateforme complète de gestion de la qualité SQL qui couvre l'audit et la gestion SQL, du développement aux environnements de production. Il prend en charge les bases de données open source, commerciales et nationales grand public, fournit des capacités d'automatisation des processus pour le développement, l'exploitation et la maintenance, améliore l'efficacité en ligne et améliore la qualité des données.
SQLE obtenir
taper | adresse |
---|---|
Dépôt | https://github.com/actiontech/sqle |
document | https://actiontech.github.io/sqle-docs/ |
publier des nouvelles | https://github.com/actiontech/sqle/releases |
Documentation de développement du plug-in d'audit des données | https://actiontech.github.io/sqle-docs/docs/dev-manual/plugins/howtouse |