MySQL 复制延迟是如何计算的?

文若2年前技术文章607

前言

日常运维中总会收到 MySQL 备库延迟告警,一般数据库监控只读实例延迟都是采集 Seconds_Behind_Master 值,我们都知道它在某些场景下不可靠,今天一起探索 MySQL 是如何计算复制延迟,可以帮助我们透过现象看到问题的本质。

一、MySQL 延迟计算方法

1. 主从复制中会导致延迟的环节

截屏2023-10-20 16.47.24.png

首先我们先过一遍 MySQL 复制过程,了解延迟可能会出现在哪个环节。请看上图,从图中我们可以了解到主从复制至少有三个环节是可能导致复制延迟的,比如 binlog dump 线程发送日志给从库 IO 线程网络环境不好造成延迟,从库 SQL 线程回放日志时,负载太高导致延迟,这种情况导致的延迟的现象 SBM 不会很高,但是一直持续降不下去,此时就要对从库做一个体检能否有 SQL 可以优化,扩容实例或者添加只读实例分担从库压力。


2. MySQL 源码中的计算方法
  1. 下方源码注释中提了主从延迟的计算公式:  源码中提到延迟复制的计算公式为:截屏2023-10-20 16.48.09.png

    /* 
    从库当前系统的时间 - 从库 SQL 线程正在执行回放 Event 的时间戳 - 主从系统(主机)之间的时间差
    */
    clock_of_slave - last_timestamp_executed_by_SQL_thread - clock_diff_with_master

    last_timestamp_executed_by_SQL_thread 从库 SQL 线程正在回放事件的时间戳,在 binlog 每个事件的 Common Header 部分都会记录事务发生时间。 clock_diff_with_master 值是主从 Server 主机的时间差,在从库 IO_thread 启动时会重新计算该值,后面使用时直接复用计算结果,相当于校准值保障主从 DateTime 是一致的。

    从公式中我们可以了解即使主从系统时间不一致 MySQL 也会计算出正确的延迟结果,在复制进行中,如果修改了系统时间会导致延迟误差,因为 clock_diff_with_master 只会在 IO 线程启动时重新计算。

    当计算延迟为负数时则直接为零。

    当从库两个线程都为 Yes 时,且 SQL 线程没有在工作的时候 Seconds_Behind_Master 直接判定为零。

    当 SQL 线程重放所有的日志后 IO 线程关闭 Seconds_Behind_Master 无法计算直接为 Null。


  1. 下方是 MySQL计算主从延迟的源代码:

    截屏2023-10-20 16.48.55.png

    long time_diff= ((long)(time(0) - mi->rli->last_master_timestamp) - mi->clock_diff_with_master);

    time(0)  指的就是本地时间; mi->rli->last_master_timestamp  从库当前正在回放 SQL event 的时间戳; mi->clock_diff_with_master 时间校准值,前面提到过;

    貌似看起来没得问题,其实是有一下坑的,比如:

    网络环境不是很好,既 IO_thread 的同步是瓶颈,而在 Slave 端来看 sql_thread 能够很快应用掉日志,而 SBM 等于 0 这就会产生一错觉,看起来是没有延迟的,其实已经产生较大延迟。

    如果 1 分钟内产生大量的 binlog 如果此时以 timestamp 做计算延迟不会很大,但实际相差的日志量 2 G 也会导致误导。此时就需要根据 master - slave 日志量差异对比才能看出问题。

二、处理主从延迟思路

1. 避免大事务

上面我们已经了解到延迟的计算公式,当 SQL 线程重放一个大事务时,SQL 线程的时间戳当于暂停了,虽然一个大事务是由很多 event 组成,也有可能这些 event 的时间戳可能完全相同(row 模式下)SQL 线程要花费较长时间去应用掉,由 SBM 计算公式得出无论主库是否有数据写入,从库的复制延迟会持续增加。避免大事务  避免大事务  避免大事务  说三遍。

2. DDL 导致的延迟

比如 BI 只读库,当主库应用一个 DDL 操作,即使是一个非常小的 INSTANT 操作,此时 BI 库中可能有大查询在跑可能会堵塞这个 DDL 提交(执行完需要获取一次 X 锁,刚好被 SQL 占用)必须等待查询完才能提交,延迟就一直蹭蹭蹭往上涨.....  此时就需用先 Kill 掉相关查询即可。

3. 合理的架构

如果业务压力确实比较大,读库负载较高,无法快速应用掉主库的日志数据,此时就需要考虑扩展从库,分担压力。

4. 多线程复制

5.6 支持库级别的 SQL_thread 回放 5.7 的 WRITESET 支持事务不冲突情况下并行回放。如果你被延迟困扰,升级到 5.7 吧。

5. 增大从库 buffer pool

增大从库 innodb_buffer_pool 参数 innodb 可以缓存更多的数据,可以减少 IO 压力。

6. 关闭从库 binlog

架构允许的情况下,可以考虑关闭从库 binlog 日志。


相关文章

Flume使用案例之Flume与Flume之间数据传递,多Flume汇总数据到单Flume

目标:flume11监控文件hive.log,flume-22监控某一个端口的数据流,flume11与flume-22将数据发送给flume-33,flume33将最终数据写入到HDFS。分步实现:1...

xargs-管道命令符

有时候我们的脚本却需要 echo '516' | kill 这样的效果,例如 ps -ef | grep 'ddd' | kill 这样的效果,筛选出符合某条件的进程pid然后结束。这种需求对于我们来...

MapReduce工作机制解析

MapReduce工作机制解析

一、MapTask工作机制主要可以分为Read阶段,Map阶段,Collect阶段,Spill阶段(1)Read阶段:MapTask通过InputFormat获得的RecordReader,从输入In...

ES运维(一)底层数据存储原理

ES运维(一)底层数据存储原理

1、ES底层数据存储原理架构图Segment工作流程:A、 新的文档在内存中组织B、 每隔一段时间,buffer将会被提交:生成一个新的segment(一个额外的新的倒序索引)并被写到磁盘,同时一个新...

 MySQL运维实战(1.2)安装部署:使用二进制安装部署

MySQL运维实战(1.2)安装部署:使用二进制安装部署

一般在生产环境,我们会使用二进制安装的方式安装MySQL。使用二进制安装,在处理单机多实例、升级MySQL等场景下更加方便。如果有特殊的需求(比如要打一些patch),我们还可以自己编译二进制。1、下...

PG的多版本并发控制(三)

三、多版本并发控制3.1 常见多版本并发的实现方式第一种方式是,数据库仅保存最新版本数据,将发生变更的旧行版本数据写到其他地方如undo,当需要读取旧版本数据时,通过undo重构。oracle和MyS...

发表评论    

◎欢迎参与讨论,请在这里发表您的看法、交流您的观点。