Loading [MathJax]/jax/input/TeX/config.js
首页
学习
活动
专区
圈层
工具
发布
首页
学习
活动
专区
圈层
工具
MCP广场
社区首页 >专栏 >源码分析 | 详解 binlog 时间戳与 exec_time 的关系

源码分析 | 详解 binlog 时间戳与 exec_time 的关系

作者头像
爱可生开源社区
发布于 2024-05-20 08:47:08
发布于 2024-05-20 08:47:08
26500
代码可运行
举报
运行总次数:0
代码可运行
作者:李锡超,苏商银行DBA,负责数据库和中间件运维和建设。擅长 MySQL、Python、Oracle,爱好骑行、技术研究和分享。

爱可生开源社区出品,原创内容未经授权不得随意使用,转载请联系小编并注明来源。

本文约 2000 字,预计阅读需要 8 分钟。

1概述

近期,某系统进行测试时,发现主从同步存在延迟,随即通过 binlog 确认延迟原因。当使用 mysqlbinlog 命令解析后,发现其中的信息“似懂非懂”。

例如,对于如下 binlog 片段:

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
# 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

通过以上 binlog 可知(P1):

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
#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):

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
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 看到的 timestamp 有什么关系呢?
  • Q4:P1 中的 timestamp 是如何取值的?特别是主从环境下

为此,通过测试验证,并结合源码分析 binlog 中常见 Event 时间与 exec_time 的由来,并总结字段之间的关系。

以下分析基于 MySQL 8.0,不同版本字段可能不同。

2主节点 binlog 日志

1. GTID Event

timestamp

对于主节点:如没有特殊说明,Event 的 timestamp 是在每个线程执行 dispatch_command() 初始位置获取最新时间戳(thd->start_time),并在生产 Event 对象时将 thd->start_time 赋值到 Log_event::common_header->when

主要堆栈信息如下:

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
|-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 获取即为提交时刻的时间戳,主节点 original_commit_timestamp 等于 immediate_commit_timestamp

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
|-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 Event

timestamp

注意:对于主节点 BEGIN event 的 timestamp 并不是执行 BEGIN 时的时间戳,而是执行第一个修改操作。在完成 InnoDB 层第一行数据修改之后,生成并写入 Table_map event。在生成 Table_map event 之前,如果此时整个事务的 binlog 缓存是空的,才会立即获取该操作的 thd->start_time,并生成真正的 BEGIN event。

exec_time

同时,对于主节点的 exec_time 就是在生成 BEGIN Event 的过程中,获取最新的时间戳 - BEGIN Event 的 timestamp 而得。

exec_time = A - B

  • A:执行第一个修改 SQL,完成第一行修改(write/update/delete)操作后,生成 BEGIN Event 的时间。
  • B:第一个修改 SQL 的开始执行时间(thd->start_time)

内部堆栈与执行顺序如下:

3. Table_map Event

4. Write Event

5. Xid Event

6. 主节点小结

  • 除了 BEGIN Event 的 timestamp 是第一个需要写入 binlog 操作(如:write/update/delete)的开始时间;
  • 其它 Event 的 timestamp 为 SQL 语句执行时的开始时间;
  • immediate_commit_timestamp/original_commit_timestamp 即为提交时的时间戳;
  • exec_time = A - B
    • A:执行第一个修改 SQL,完成第一行修改(write/update/delete)操作后,生成 BEGIN Event 的时间。
    • B:第一个修改 SQL 的开始执行时间(thd->start_time)

3从节点 binlog 日志

1. GTID Event

timestamp

在从节点:对于 GTID Event,MySQL 在解析 Event 时,并不会获取主节点 GTID/XID Event 的时间戳,因此会“继承”该事务上一个操作的时间戳。而从节点所有修改操作的时间戳都来自于主节点执行操作时的时间戳。因此从节点的 GTID/XID Event 的时间即为主节点最后一个修改操作的 timestamp。

immediate_commit_timestamp/original_commit_timestamp

immediate_commit_timestamp 获取从节点提交时刻的时间戳。original_commit_timestamp 从 GTID Event 中的 original_commit_timestamp 获取,即为主节点提交操作的 timestamp

主要堆栈信息如下:

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
|-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)

公式

immediate_commit_timestamp - original_commit_timestamp = A + B + C

  • A = 主节点 传输 binlog 到 从节点 的耗时
  • B = 从节点 重放 binlog 的耗时
  • C = 同步延迟/中断的耗时

2. BEGIN Event

timestamp

这里的 timestamp 来自于主节点 BEGIN Event 的 timestamp。其实际执行时,是会获取 BEGIN Event 的 timestamp 将其赋值给 thd->start_time/thd->user_time。从节点生成 Event 对象时,继续从 thd->start_time 获取时间戳即可。

exec_time

然后,从节点的 exec_time 依然是生成 BEGIN Event 的过程中,获取 最新的时间戳 - timestamp 而得到(注意这里的 timestamp 来自于主节点修改 SQL 的开始执行时间)。

主要堆栈信息如下:

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
|-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)
            ...

公式

exec_time = A + B + C + D

  • A = 主节点 整个事务的耗时
  • B = binlog 传输耗时
  • C = 同步延迟/中断耗时(可能-主要)
  • D = 从节点完成第一行数据修改

original_commit_timestamp - begin event 的 timestamp = 表示主节点整个事务的实际耗时(【主-第一个修改】 到【主- commit 开始】)。

3. Table_map Event

4. Write Event

5. Xid Event

6. 从节点小节

  • 除了 GTID/XID Event,其它 Event 的时间戳均来自于主节点的 Event;
  • GTID/XID Event 的 timestamp 为主节点最后一个修改操作开始时间;
  • GTID Event 的 original_commit_timestamp 来自于主节点,immediate_commit_timestamp 为最新的时间戳;
  • exec_time = A - B
    • A = 从节点 生成 BEGIN Event 的最新时间戳
    • B = 主节点 执行第一个 DML 操作的开始时间

4结语

至此,关于 binlog 中的时间戳与 exec_time 已基本梳理完成,有兴趣的朋友可以回到文章开头,再看看 Q1-Q3 是否有了答案。

最后,建议读者朋友实际模拟几个案例,以便于更加深刻的理解相关字段,后续在利用 binlog 分析主从同步问题时,能更加得心应手。

以上信息仅供交流,作者水平有限,如有不足之处,欢迎在评论区交流。

本文关键字:#MySQL# #binlog# #源码#

本文参与 腾讯云自媒体同步曝光计划,分享自微信公众号。
原始发表:2024-05-14,如有侵权请联系 cloudcommunity@tencent.com 删除

本文分享自 爱可生开源社区 微信公众号,前往查看

如有侵权,请联系 cloudcommunity@tencent.com 删除。

本文参与 腾讯云自媒体同步曝光计划  ,欢迎热爱写作的你一起参与!

评论
登录后参与评论
暂无评论
推荐阅读
编辑精选文章
换一批
Spring Security 6.x 过滤器链SecurityFilterChain是如何工作的
上一篇主要介绍了Spring Secuirty中的过滤器链SecurityFilterChain是如何配置的,那么在配置完成之后,SecurityFilterChain是如何在应用程序中调用各个Filter,从而起到安全防护的作用,本文主要围绕SecurityFilterChain的工作原理做详细的介绍。
fullstackyang
2024/06/02
5950
Spring Security 6.x 过滤器链SecurityFilterChain是如何工作的
Spring Security(六)—SpringSecurityFilterChain加载流程深度解析
SpringSecurityFilterChain 作为 SpringSecurity 的核心过滤器链在整个认证授权过程中起着举足轻重的地位,每个请求到来,都会经过该过滤器链,前文《Spring Security(四)--核心过滤器源码分析》 中我们分析了 SpringSecurityFilterChain 的构成,但还有很多疑问可能没有解开:
kirito-moe
2018/07/25
8K0
Spring Security(六)—SpringSecurityFilterChain加载流程深度解析
SpringSecurity6 | 委派筛选器代理和过滤器链代理
大家好,我是Leo哥🫣🫣🫣,上一节我们简单回顾了一下关于Servlet原生过滤器以及简单认识了SpringSecurity中的一些过滤器。但是底层SpringSecurity是如何维护这些过滤器,并通过这些过滤器是如果拦截我们的客户端请求的,我们都还只是停留在表层,今天就让我们去深入了解一下我们今天得主角—委派筛选器代理 DelegatingFilterProxy。好了,话不多说让我们开始吧😎😎😎。
程序员Leo
2023/11/16
3790
SpringSecurity6 | 委派筛选器代理和过滤器链代理
Spring Security 实战干货:图解Spring Security的过滤器体系
我在Spring Security 实战干货:内置 Filter 全解析对Spring Security的内置过滤器进行罗列,但是Spring Security真正的过滤器体系才是我们了解它是如何进行"认证"、“授权”、“防止利用漏洞”的关键。
码农小胖哥
2020/07/06
4.2K0
【第三篇】开始和SpringSecurity的简单交往-处理请求的本质
分析SpringSecurity的核心原理,那么我们从哪开始分析?以及我们要分析哪些内容?
用户4919348
2022/05/10
3410
【第三篇】开始和SpringSecurity的简单交往-处理请求的本质
SpringSecurity:高级应用
分析SpringSecurity的核心原理,那么我们从哪开始分析?以及我们要分析哪些内容?
Java鱼头
2022/11/30
8721
SpringSecurity是如何玩弄过滤器链的
本文适合在对SpringSecurity有基本认识,并且会基础使用的,想要进阶研究源码的小伙伴。
大忽悠爱学习
2022/10/06
8800
SpringSecurity是如何玩弄过滤器链的
SpringSecurity6从入门到实战之整合原生Filter链
Spring提供了一个名为DelegatingFilterProxy的过滤器实现,它允许在Servlet容器的生命周期和Spring的ApplicationContext之间架起桥梁。Servlet容器允许使用自己的标准注册过滤器实例,但它不知道Spring定义的Bean。您可以通过标准的Servlet容器机制注册DelegatingFilterProxy,但将所有工作委托给实现过滤器的Spring Bean
全干程序员demo
2024/05/31
4990
SpringSecurity6 | 核心过滤器
大家好,我是Leo哥🫣🫣🫣,上一节我们通过源码剖析以及图文分析,了解了关于委派筛选器代理和过滤器链代理的原理和作用。这节课我们接着学习SpringSecurity的过滤器,了解SpringSecurity中都有哪些核心过滤器。好了,话不多说让我们开始吧😎😎😎。
程序员Leo
2023/11/17
1.5K0
SpringSecurity6 | 核心过滤器
Spring Security过滤器链体系
上面两个疑问的答案就在配置类SpringBootWebSecurityConfiguration中。你可以按照下面这个思维导图去理解这个自动配置:
码农小胖哥
2022/02/11
6420
Spring Security过滤器链体系
源码剖析 Spring Security 的实现原理
Spring Security 是一个轻量级的安全框架,可以和 Spring 项目很好地集成,提供了丰富的身份认证和授权相关的功能,而且还能防止一些常见的网络攻击。
架构狂人
2023/11/22
7740
源码剖析 Spring Security 的实现原理
深入理解 FilterChainProxy【源码篇】
松哥最近也是特别忙,Security 更新慢下来了,但是秉持前面说的,要学就成系列的学,要学就学透彻,这个系列我还会继续更下去。
江南一点雨
2020/07/21
1.9K1
深入理解 FilterChainProxy【源码篇】
Spring Security入门(二) 基于内存存储的表单登录实战
Spring Security 对Servlet的安全认证是基于包含一系列的过滤器对请求进行层层拦截处理实现的,多个过滤器组成过滤器链。处理单个http 请求的过滤链角色示意图如下所示:
用户3587585
2022/09/21
8940
Spring Security入门(二) 基于内存存储的表单登录实战
Spring Security 上
FilterSecurityInterceptor:是一个方法级的 权限过滤器 ,基本位于过滤链的最底部
张小驰出没
2021/05/08
6990
Spring Security  上
Spring Security的过滤器链机制
在“码农小胖哥”的文章中提到一个关键的过滤器链SecurityFilterChain,当一个请求 HttpServletRequest 进入 SecurityFilterChain 时,会通过 matches 方法来确定是否满足条件进入过滤器链,进而决定请求应该执行哪些过滤器。下面我们自己来梳理一遍。
阿提说说
2022/12/02
9710
Spring MVC更多家族成员---框架内处理流程拦截与HandlerInterceptor---08
前面已经讲述了,HandlerMapping返回的用于处理具体Web请求的Handler对象,是通过一个HandlerExecutionChain对象进行封装的(这在HandlerMapping的接口定义上可以看出来)。
大忽悠爱学习
2022/08/23
5950
Spring MVC更多家族成员---框架内处理流程拦截与HandlerInterceptor---08
SpringSecurity6 | 回顾Filter
大家好,我是Leo哥🫣🫣🫣,上一节我们通过一个HelloWorld案例,以代码的方式实现了我们项目添加登录鉴权功能,只是通过一个就轻松实现了这个功能。那么他其中的原理是什么呢,带着疑问,我们后面几节课主要学习一下如何实现这些的原理。好了,话不多说让我们开始吧😎😎😎。
程序员Leo
2023/11/16
3340
SpringSecurity6 | 回顾Filter
【译】Spring 官方教程:Spring Security 架构
原文:Spring Security Architecture 译者:徐靖峰 校对:马超君 专题指南 本文是 Spring Security 的入门指南,并对 Spring Security 的框架设计和基础组件进行深度解析。我们仅涉及应用程序安全性的基础知识,但这已足够消除开发人员在使用 Spring Security 时遇到的一些困惑。要做到这一点,我们需要了解如何使用过滤器和方法注解来保障Web应用程序的安全性。如果你需要了解高级别安全应用程序的工作方式,以及如何定制安全应用程序,或只需要学习如何思考
程序猿DD
2018/03/26
2K0
【译】Spring 官方教程:Spring Security 架构
Spring Security 实战:Spring Boot 下的自动配置
我们在前几篇对 Spring Security 的用户信息管理机制,密码机制进行了探讨。没有看的同学可通过 https://felord.cn 获取。我们发现 Spring Security Starter相关的 Servlet 自动配置都在spring-boot-autoconfigure-2.1.9.RELEASE(当前 Spring Boot 版本为2.1.9.RELEASE) 模块的路径org.springframework.boot.autoconfigure.security.servlet 之下。其实官方提供的Starter组件的自动配置你都能在spring-boot-autoconfigure-2.1.9.RELEASE下找到。今天我们进一步来解密 Spring Security 在 Spring Boot 的配置和使用。
码农小胖哥
2019/12/10
1.6K0
SpringSecurity常用过滤器介绍
  首当其冲的一个过滤器,非常重要 主要是使用SecurityContextRepository在session中保存或更新一个SecurityContext,并将SecurityContext给以后的过滤器使用,来为后续filter建立所需的上下文,SecurityContext中存储了当前用户的认证和权限信息。
用户4919348
2019/12/11
1.7K0
SpringSecurity常用过滤器介绍
相关推荐
Spring Security 6.x 过滤器链SecurityFilterChain是如何工作的
更多 >
领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档
本文部分代码块支持一键运行,欢迎体验
本文部分代码块支持一键运行,欢迎体验