备库Seconds_Behind_Master的计算
背景
在mysql主备环境下,主备同步过程如下,主库更新产生binlog, 备库io线程拉取主库binlog生成relay log。备库sql线程执行relay log从而保持和主库同步。

理论上主库有更新时,备库都存在延迟,且延迟时间为备库执行时间+网络传输时间即t4-t2。
那么mysql是怎么来计算备库延迟的?
先来看show slave status中的一些信息
io线程拉取主库binlog的位置
Master_Log_File: mysql-bin。000001
Read_Master_Log_Pos: 107
sql线程执行relay log的位置
Relay_Log_File: slave-relay。000003
Relay_Log_Pos: 253
sql线程执行的relay log相对于主库binlog的位置
Relay_Master_Log_File: mysql-bin。000001
Exec_Master_Log_Pos: 107
源码实现
Seconds_Behind_Master计算的源码实现如下
if ((mi->get_master_log_pos() == mi->rli->get_group_master_log_pos()) &&
(!strcmp(mi->get_master_log_name(), mi->rli->get_group_master_log_name())))
{
if (mi->slave_running == MYSQL_SLAVE_RUN_CONNECT)
protocol->store(0LL);
else
protocol->store_null();
}
else
{
long time_diff= ((long)(time(0) - mi->rli->last_master_timestamp)
- mi->clock_diff_with_master);
protocol->store((longlong)(mi->rli->last_master_timestamp ? max(0L, time_diff) : 0));
}
大致可以看出是通过时间和位点来计算的,下面详细分析下
if里面条件表示如果io线程拉取主库binlog的位置和sql线程执行的relay log相对于主库binlog的位置相等,那么认为延迟为0。 一般情况下,io线程比sql线程快。但如果网络状况特别差,导致sql线程需等待io线程的情况,那么这两个位点可能相等,会导致误认为延迟为0。
再看else里,
clock_diff_with_master io线程启动时会向主库发送sql语句“SELECT UNIX_TIMESTAMP()”,获取主库当前时间,然而用备库当前时间减去此时间或者主备时间差值即为clock_diff_with_master。 这里如果有用户中途修改了主库系统时间或修改了timestamp变量,那么计算出备库延迟时间就是不准确的。
last_master_timestamp 表示主库执行binlog事件的时间。 此时间在并行复制和非并行复制时的计算方法是不同的 非并行复制: 备库sql线程读取了relay log中的event,event未执行之前就会更新last_master_timestamp。这里时间的更新是以event为单位。
rli->last_master_timestamp= ev->when。tv_sec + (time_t) ev->exec_time;ev->when。tv_sec表示事件的开始时间。 exec_time指事件在主库的执行时间,只有Query_log_event和Load_log_event才会统计exec_time。 另外一种情况是sql线程在等待io线程获取binlog时,会将last_master_timestamp设为0。 last_master_timestamp为0时,按上面的算法Seconds_Behind_Master为0。此时任务备库是没有延迟的。
并行复制
并行复制有一个分发队列gaq,sql线程将binlog事务读取到gaq,然后再分发给worker线程执行。并行复制时,binlog事件是并发穿插执行的,gaq中有一个checkpoint点称为lwm, lwm之前的binlog都已经执行,而lwm之后的binlog有些执行有些没有执行。 假设worker线程数为2, gap有1,2,3,4,5,6,7,8个事务。 worker 1已执行的事务为1 4 6 ,woker 2执行的事务为2 3 。 那么lwm为4。
并行复制更新gap checkpiont时,会推进lwm点,同时更新last_master_timestamp为lwm所在事务结束的event的时间。 因此,并行复制是在事务执行完成后才更新last_master_timestamp,更新是以事务为单位。
同时更新gap checkpiont还受slave_checkpoint_period参数的影响。
这导致并行复制下和非并行复制统计延迟存在差距,差距可能为slave_checkpoint_period+ 事务在备库执行的时间。这就是为什么在并行复制下有时候会有很小的延迟,而改为非并行复制时反而没有延迟的原因。
另外当sql线程等待io线程时且gaq队列为空时,会将last_master_timestamp设为0。同样此时认为没有延迟,计算得出seconds_Behind_Master为0
位点信息维护
io线程拉取binlog的位点
Master_Log_File 读取到主库ROTATE_EVENT时会更新(process_io_rotate)
Read_Master_Log_Pos:io线程每取到一个event都会从event中读取pos信息并更新
mi->set_master_log_pos(mi->get_master_log_pos() + inc_pos);sql线程执行relay log的位置
Relay_Log_File
sql线程处理ROTATE_EVENT时更新(Rotate_log_event::do_update_pos)
Relay_Log_Pos:
非并行复制时,每个语句执行完成更新(stmt_done)
并行复制时,事务完成时更新(Rotate_log_event::do_update_pos/ Xid_log_event::do_apply_event/stmt_done)sql线程执行的relay log相对于主库binlog的位置
Relay_Master_Log_File
sql线程处理ROTATE_EVENT时更新(Rotate_log_event::do_update_pos)
Exec_Master_Log_Pos 和Relay_Log_Pos同时更新
非并行复制时,每个语句执行完成更新(stmt_done)
并行复制时,事务完成时更新(Rotate_log_event::do_update_pos/ Xid_log_event::do_apply_event/stmt_done)
谈到位点更新就有必要说到两个事件:HEARTBEAT_LOG_EVENT 和 ROTATE_EVENT
- HEARTBEAT_LOG_EVENT
HEARTBEAT_LOG_EVENT我们的了解一般作用是,在主库没有更新的时候,每隔master_heartbeat_period时间都发送此事件保持主库与备库的连接。 而HEARTBEAT_LOG_EVENT另一个作用是,再gtid模式下,主库有些gtid备库已经执行同时,这些事件虽然不需要再备库执行,但读取和应用binglog的位点还是要推进。 因此,这里将这类event转化为HEARTBEAT_LOG_EVENT,由HEARTBEAT_LOG_EVENT帮助我们推进位点。
- ROTATE_EVENT
主库binlog切换产生的ROTATE_EVENT,备库io线程收到时会也有切换relay log。此rotate也会记入relay log,sql线程执行ROTATE_EVENT只更新位点信息。 备库io线程接受主库的HEARTBEAT_LOG_EVENT,一般不用户处理。前面提到,gtid模式下,当HEARTBEAT_LOG_EVENT的位点大于当前记录的位点时,会构建一个ROTATE_EVENT,从而让sql线程推进位点信息。
if (mi->is_auto_position() && mi->get_master_log_pos() < hb。log_pos
&& mi->get_master_log_name() != NULL)
{
mi->set_master_log_pos(hb。log_pos);
write_ignored_events_info_to_relay_log(mi->info_thd, mi); //构建ROTATE_EVENT
......
}
另外,在replicate_same_server_id为0时,备库接收到的binlog与主库severid相同时,备库会忽略此binlog,但位点仍然需要推进。为了效率,此binlog不需要记入relay log。而是替换为ROTATE_EVENT来推进位点。
延迟现象
初始主备是同步的,且没有任何更新。 假设主备库执行某个DDL在都需要30s,执行某个大更新事务(例如insert..select * from )需要30s。
不考虑网络延迟。

- 非并行复制时
执行DDL: t2时刻主库执行完,t2时刻备库执行show slave status,Seconds_Behind_Master值为0。同时t2至t3 Seconds_Behind_Master依次增大至30,然后跌0。
执行大事务:t2时刻主库执行完,t2时刻备库执行show slave status,Seconds_Behind_Master值为30。同时t2至t3 Seconds_Behind_Master依次增大至60,然后跌0。
以上区别的原因是exec_time只有Query_log_event和Load_log_event才会统计,普通更新没有统计导致。
- 并行复制时
执行DDL: t2时刻主库执行完,t2至t3备库执行show slave status,Seconds_Behind_Master值一直为0
执行大事务:t2时刻主库执行完,t2至t3备库执行show slave status,Seconds_Behind_Master值一直为0
这是因为执行语句之前主备是完全同步的,gaq队列为空,会将last_master_timestamp设为0。而执行DDL过程中,gap checkpoint一直没有推进,last_master_timestamp一直未0,直到DDL或大事务完成。 所以t2至t3时刻Seconds_Behind_Master值一直为0。而t3时刻有一瞬间last_master_timestamp是会重置的,但又因slave_checkpoint_period会推进checkpoint,gaq队列变为空,会将last_master_timestamp重设为0。 因此t3时刻可能看到瞬间有延迟(对于DDL是延迟30s,对于大事务时延迟60s)。
这似乎很不合理,gaq队列为空,会将last_master_timestamp设为0,这条规则实际可以去掉。
相关bug
BUG#72376, PREVIOUS_GTIDS_LOG_EVENT事件记录在每个binlog的开头,表示先前所有文件的gtid集合。relay-log本身event记录是主库的时间,但relay log开头的PREVIOUS_GTIDS_LOG_EVENT事件,是在slave端生成的,时间也是以slave为准的。 因此不能用此时间计算last_master_timestamp。 修复方法是在relay log写PREVIOUS_GTIDS_LOG_EVENT事件是标记是relay log产生的,在统计last_master_timestamp时,发现是relay产生的事件则忽略统计。
if (is_relay_log)
prev_gtids_ev。set_relay_log_event();
......
if (!(ev->is_artificial_event()||...))
rli->last_master_timestamp= ev->when。tv_sec + (time_t) ev->exec_time;
总结
Seconds_Behind_Master的计算并不准确和可靠。并行复制下Seconds_Behind_Master值比非并行复制时偏大。因此当我们判断备库是否延迟时,根据Seconds_Behind_Master=0不一定可靠。但是,当我们进行主备切换时,在主库停写的情况下,我们可以根据位点来判断是否完全同步。 如果(Relay_Master_Log_File,Exec_Master_Log_Pos)和(Relay_Master_Log_File,Read_Master_Log_Pos)位置相等且Seconds_Behind_Master=0,那么我们可以认为主备是完成同步的,可以进行切换。
备库Seconds_Behind_Master的计算的更多相关文章
- MySQL · 答疑解惑 · 备库Seconds_Behind_Master计算
背景 在mysql主备环境下,主备同步过程如下,主库更新产生binlog, 备库io线程拉取主库binlog生成relay log.备库sql线程执行relay log从而保持和主库同步. 理论上主库 ...
- 【转载】mysql主键的缺少导致备库hang
最近线上频繁的出现slave延时的情况,经排查发现为用户在删除数据的时候,由于表主键的主键的缺少,同时删除条件没有索引,或或者删除的条件过滤性极差,导致slave出现hang住,严重的影响了生产环境的 ...
- mysql主键的缺少导致备库hang
最近线上频繁的出现slave延时的情况,经排查发现为用户在删除数据的时候,由于表主键的主键的缺少,同时删除条件没有索引,或或者删除的条件过滤性极差,导致slave出现hang住,严重的影响了生产环境的 ...
- OOM导致的备库raylog损坏导致主从复制异常
问题发现告警数据库出现复制中断,延迟超过100秒 问题排查复制信息检查,通过’show slave status\G’命令可以查看复制线程详细的工作状态,对于判断复制中断的原因有一些指导性意义.当时的 ...
- 44 答疑(三)--join的写法/Simple nested loop join的性能问题/Distinct和group by的性能/备库自增主键问题
44 答疑(三) Join的写法 35节介绍了join执行顺序,加了straight_join,两个问题: --1 如果用left join,左边的表一定是驱动表吗 --2 如果两个表的join包含多 ...
- Oracle备库TNS连接失败的分析
今天在测试12c的temp_undo的时候,准备在备库上测试一下,突然发现备库使用TNS连接竟然失败. 抛出的错误如下: $ sqlplus sys/oracle@testdb as sysdba S ...
- Oracle 11.2.4.0 ACTIVE DATAGUARD 单实例安装(COPY创建备库)
Oracle 11.2.4.0 ADG 单实例安装(COPY创建备库) 规划: 主: OS: Linux Centos 6.5 X64 hostname:ORA11G-DG1 ipaddress:19 ...
- oracle 利用flashback将备库激活为read wirte(10g 及上)
oracle 利用flashback将备库激活为read wirte(10g 及上) 环境: OS: CENTOS 6.5 X64 DB: ORACLE 10.2.0.5 主库操作: SQL> ...
- dataguard 归档丢失(主库中无此丢失归档处理),备库基于SCN恢复
dataguard 归档丢失(主库中无此丢失归档处理),备库基于SCN恢复 环境: OS: CentOS 6.5 DB: Oracle 10.2.0.5 1.主备库环境 主库: SQL> sel ...
随机推荐
- jQuery系列之操作select标签
每次看完东西基本就忘了,现在决定写一下博客来记录,不知道效果咋样. 一.jQuery操作选择器 1.基本选择器 关于基本选择器,我就不用太多说了,包括了ID.类.标签等选择器. 2.层次选择器 我觉得 ...
- 用clock()函数计算多项式的运行时间
百度百科中定义clock():clock()是C/C++中的计时函数,而与其相关的数据类型是clock_t.在MSDN中,查得对clock函数定义如下: clock_t clock(void) ; 简 ...
- 用Action的属性接受参数
版本, @Override是Java5的元数据,自动加上去的一个标志,告诉你说下面这个方法是从父类/接口 继承过来的,需要你重写一次,这样就可以方便你阅读,也不怕会忘记@Override是伪代码,表示 ...
- MySQL安装之后没有MySQL数据库的原因
mysql安装完之后,登陆后发现只有两个数据库:mysql> show databases;+--------------------+| Database |+------ ...
- Oracle 中的作业队列和队列调度
一,启动执行作业的进程 在 Oracle 中,是使用 “作业队列协调进程(CJQ0)” 这个协调数据库实例的作业队列的后台进程,来监视作业队列中的作业表(JOB$),并启动作业队列进程(J ...
- svn安装与其服务器搭建
1.概述:SVN为程序开发团队常用的代码管理,版本控制软件. 2.工具: 1) TortoiseSVN-1.8.4.24972-win32-svn-1.8.5.msi SVN安装包. 2)setup ...
- Dreamweaver 升级问题汇总
Adobe的产品开始从CC (Creative Cloud) 开始转向云平台,CS将逐渐成为过去时.不过CC并不限制用户在同一台机器上同时使用CS和CC,这种策略估计要持续较长一段时间. If you ...
- SQL 列转行的实现
--列转行,逗号拼接指定列的值Oracle中写法:select wmsys.wm_concat(Field1) from TableASQL Server中写法:SELECT STUFF(( SELE ...
- C# 自定义FileUpload控件
摘要:ASP.NET自带的FileUpload控件会随着浏览器的不同,显示的样式也会发生改变,很不美观,为了提高用户体验度,所以我们会去自定义FileUpload控件 实现思路:用两个Button和T ...
- [04]APUE:文件与目录
[a] stat / lstat / fstat #include <sys/stat.h> int stat(const char *restrict pathname, struct ...