再议Seconds_Behind_Master

两年前就写过一篇文章解释Seconds_Behind_Master代表的含义以及它为什么不准确,今天同事高老师又提了一个有趣的问题:Seconds_Behind_Master到底是怎么计算的呢?高老师还特地去翻了一下源码来解释,我发现我之前的理解还是有出入的,于是自己也动手去翻了一下源码,下面就来更全面的解释一下它是怎么计算的,为什么不能完全可信。

我平时读MySQL源码比较少,一般来说通过源码也是查一些基本的问题,对于我来说如果对关键代码位置不熟,比较快捷的方法就是cd到源码的根目录,然后grep "Seconds_Behind_Master" . -R -n。结果如下:

./sql/rpl_rli.cc:1209:      Seconds_Behind_Master - not critical).

./sql/slave.cc:1345:                      "do not trust column Seconds_Behind_Master of SHOW "

./sql/slave.cc:1874:  field_list.push_back(new Item_return_int("Seconds_Behind_Master", 10,

./sql/slave.cc:1963:      Seconds_Behind_Master: if SQL thread is running and I/O thread is

./sql/slave.cc:3254:    alive and connected, this is going to make Seconds_Behind_Master be 0

./sql/slave.cc:3258:    Seconds_Behind_Master grows. No big deal.

./sql/slave.cc:4666:          We say in Seconds_Behind_Master that we have "caught up". Note that

./sql/slave.cc:4679:          Seconds_Behind_Master would be zero only when master has no

从搜索结果来看,主要设计到sql/slave.cc以及sql/rpl_rli.cc,而恰好这两个文件已经可以解决我们的疑惑了。

首先第一个问题,Seconds_Behind_Master到底是怎么计算的?

我们先来看手册上的解释:

This field is an indication of how “late” the slave is:

When the slave is actively processing updates, this field shows the difference between the current timestamp on the slave and the original timestamp logged on the master for the event currently being processed on the slave.

When no event is currently being processed on the slave, this value is 0.

大意就是如果slave正在处理更新,那么sbm的计算方式就是:slave当前的时间戳-正在执行更新的binlog event上附带的timestamp.如果slave没有处理更新,那么sbm=0

那么这么解释对吗?答案是基本上算对,但是里面没解释一些细节。然后我们通过在slave.cc里面找到这个真正的计算公式:

1962     /*
1963       Seconds_Behind_Master: if SQL thread is running and I/O thread is
1964       connected, we can compute it otherwise show NULL (i.e. unknown).
1965     */
1966     if ((mi->slave_running == MYSQL_SLAVE_RUN_CONNECT) &&
1967         mi->rli.slave_running)
1968     {
1969       long time_diff= ((long)(time(0) - mi->rli.last_master_timestamp)
1970                        - mi->clock_diff_with_master);
此处省略很多注释
1991       protocol->store((longlong)(mi->rli.last_master_timestamp ?
1992                                  max(0, time_diff) : 0));
1993     }
1994     else
1995     {
1996       protocol->store_null();
1997     }

关键的代码如下:

1. long time_diff= ((long)(time(0) - mi->rli.last_master_timestamp)
                        - mi->clock_diff_with_master);
2. protocol->store((longlong)(mi->rli.last_master_timestamp ?
                                  max(0, time_diff) : 0)); 

到这里终于看到传说中的sbm计算方法了,这里解释各个变量的含义:

time(0) //从库当前系统时间戳,Linux系统函数

mi->rli.last_master_timestamp //当前从库正在执行语句binlog event时间戳

mi->clock_diff_with_master //主从系统时间戳的差值,slave-master

到这里我们就知道手册上其实描述不准确,还少了一部分clock_diff_with_master,因为主库上记录binlog event的时间戳与从库上计算本地时间戳time(0)都是调用系统的时间函数,而此时假如说主从时间设置不一致,那么这个值不就完全没意义了吗?因此为了尽量避免这种情况出现,每次在从库与主库建立连接的时候都会获取主从的时间戳,然后算出一个差值作为一个常量保存,以后每次算sbm时都会减去这个值。但是有一个问题存在,MySQL只会在建立主从复制连接的时候算这个值,以后都不会再更新,因此假如说主从连接建立好了后去更改主从时间设置,比如NTPD,比如set
timestamp=xxx之类的操作,那么此时看到的sbm的值就会更加不可靠了。

在正常情况下第一句就已经是我们show slave status看到的sbm值了,那么第二句是为了解决什么问题呢?

1. clock_diff_with_master是主从SELECT UNIX_TIMESTAMP()的差值,很有可能主库执行的时候是1,从库执行的时候是2(因为并不能保证主从是同一时刻执行),那么此时clock_diff_with_master=1, 那么假设此时time(0)-last_master_timestamp的值等于0,那么0-1=-1,而-1会给用户歧义,于是官方在这种情况下会强制把负值变成0

2. 前面说了sbm的值分两种情况,从库有SQL线程在处理语句时计算方法刚详细解释过,从库SQL线程没有处理语句是把这个值设为0,那么第二句代码就实现了这个功能,第二句的三元表达式告诉我们,当mi->rli.last_master_timestamp值为0的时候,sbm=0。那么mi->rli.last_master_timestamp值的更新逻辑是怎样的呢?

主要有两个地方:

其一,rpl_rli.cc/Relay_log_info::stmt_done

每次从relay log解析出一条binlog event执行时,last_master_timestamp= event_creation_time;

其二,slave.cc/static Log_event* next_event(Relay_log_info* rli)

进入到下面逻辑的前提条件是relay log已经执行完了,SQL线程在等待relay log有更新

4686         time_t save_timestamp= rli->last_master_timestamp; //先把最后一次处理的binlog event timestamp保存起来,等将来主库又推了binlog过来后第一次计算sbm时就可以用了,这也是为什么有时候start slave后第一次show slave status看到sbm值非常大的一个原因
4687         rli->last_master_timestamp= 0;  //把值设为0,那么前面的三元表达式计算的sbm就是0
... 省略部分代码
4779         rli->relay_log.wait_for_update_relay_log(rli->sql_thd); //等待relay log有更新
4780         // re-acquire data lock since we released it earlier
4781         mysql_mutex_lock(&rli->data_lock);
4782         rli->last_master_timestamp= save_timestamp; //重新设置回原值
4783         continue;   

然后再来第二个大问题:Seconds_Behind_Master是否可靠?其值为0是否表示主从数据完全一致?

答案必然是否定的。

1. sbm是表示的是relay log中event的延时,而MySQL默认复制是异步的,因此可能从库relay log执行完,但是主从由于网络以及这种原因主库上的binlog没有推送过来,而且我们实际线上也遇到过由于网络原因sbm=0,但主库的binlog根本就没推送过来,一般此时通过stop slave/start slave能发现这种假象。所以平常我们去监控主从延时也不会直接用sbm做判断标准,而是主从建立一张heartbeat表,往主库插入一条数据来判断延时情况。

2. 如果主从时间不同步,那么可能导致sbm延时值不准确

3. 5.6如果开启了多线程复制,那么这个值就更加不准了。我这里也很好奇大家的线上5.6如果开启了MTS是怎么来监控主从延时的?

--EOF--

时间: 2024-10-01 03:06:51

再议Seconds_Behind_Master的相关文章

再议 js 数字格式之正则表达式

原文:再议 js 数字格式之正则表达式 前面我们提到到了js的数字格式<浅谈 js 数字格式类型>,之前的<js 正则练习之语法高亮>里也提到了优化数字匹配的正则.不过最近落叶给了我一个正则,让我豁然开朗,比我写的犀利多了,所以今天拿出来简单说一下(只说十进制部分的匹配). 先看下我之前写的正则:/\d+(?:\.\d+)?(?:[eE][+-]?\d+)?|\.\d+(?:[eE][+-]?\d+)?/落叶在 jQuery 中发现的正则: /(?:\d*\.|)\d+(?:[eE

第十节课:再议数据结构与数据类型

一.list, dict, tuple之它们其实是好基友 1. 没有最优秀的数据结构,只有最适用的. 2. 了解每个数据结构才能活学活用 3. 它们相互转化如此简单 ps:如果一个问题能用Python的内置数据类型解决,推荐使用内置的数据类型,这样做的效率是比较好的. list[1,2,3,4] 是有序的 list[0] list[1] list[2] 如果涉及到的问题是有顺序的就可以考虑使用list 字典是键值(本质上list也是键值,键就是游标,只不过是整型), 但字典的键可以是字符串(语义

再议指针---------函数回调(qsort函数原理)

我们能否写一个这样的函数: 可以对任何类型数据排序 任何人在使用该函数不需要修改该函数代码(即:用户可以不必看到函数源 码,只会调用就行) 思考: 用户需要排序的数据的类型千变万化,可能是int型,也有可能是自定义的结构体类型,各种类型的大小比较规则是不一样的,这样看来实现一个这样全能的排序函数似乎不可能. 但具体需要排序的类型应按照什么规则确定大小只有使用该函数的用户最清楚,那我们可不可以把实现比较大小的功能交给用户来完成了,到时候用户只需告诉该函数比较规则(函数)在什么位置,这样排序函数不就

页面前端的水有多深?再议页面开发

但凡从事互联网的人基本都会写几行html,用过Word的人用Dreamweaver也能做出规整的页面,所以大部分人会很自然地认为"页面 的开发没什么技术含量,很简单".不仅有这种普遍的认知,对从业者来说也有很多疑惑:做页面前端实现,没问题:兼容性,小case:图片集成,一直都在 用--还能有什么问题?瓶颈啊.天花板啊.转型啊.出路啊就在从业者中广泛讨论.是不是真的没什么问题了呢?网易邮箱前端技术中心也设立好几年了,似乎有 着讨论不完的话题,也经常会有一些新的想法让大家为之一振.那么页面

再议Citrix PVS架构与机制

一.PVS是存储架构和网络计算架构 我们说Citrix PVS架构本质上是一个存储架构,是因为在Citrix PVS架构下,实现了计算和存储的分离.首先在传统的计算机上,计算资源和存储资源是同处于相同计算机内部的通过高速总线连接起来的组件,而在存储设备上,服务器本地的存储资源不在用于存储数据文件,存储数据文件的存储空间通过网络(TCP/IP or FC)传送到专门的存储控制器,由存储控制器来分配和管理这些元数据和IO,并最终将数据落地到硬盘空间存储起来.所以从原理出发,Citrix PVS就相当

再议Swift操作符重载

今天我们来谈一谈Swift中的操作 符重载,这一功能非常实用,但是也相当有风险.正所谓“能力越大责任越大”,这句话用来形容操作符重载最合适不过了.它可以令你的代码更加简洁,也可以让 一个函数调用变得又臭又长.而对于那些没怎么读过你的代码的人来说,操作符的使用同时也会让代码的可读性大打折扣. 谨 慎引入,按需使用.比如在连接两个字串的时候你就可以通过重载加法来实现.甚至于你仅在屏幕上输入一个加号,就能响应一个网络链接.播放一段音乐或者完成 你能实现的其他任何功能.然而过于复杂的功能对编码来说简直就

再议Matlab归一化函数

在最新版的matlab里面共有两个归一化函数:mapminmax()和mapstd(). mapminmax()函数将数据归一化到[-1 1](默认也可自己调参数). mapstd()函数将数据归一化成零均值和单位方差. (1) a = -0.9200    0.7300   -0.4700    0.7400    0.2900 [y,ps] = mapminmax(a) y = -1.0000    0.9880   -0.4578    1.0000    0.4578 ps = name

再议ASP.NET MVC中CheckBoxList的验证

在ASP.NET MVC 4中谈到CheckBoxList,经常是与CheckBoxList的显示以及验证有关.我在"MVC扩展生成CheckBoxList并水平排列"中通过扩展HtmlHelper做到了水平或垂直显示CheckBoxList.在"MVC生成CheckBoxList并对其验证"中,借助模版实现对一组CheckBoxList的验证,但如果要对多组CheckBoxList验证,这种方法也不是很好. 比如,在电商商品模块中,关于某个类别下会有多个属性,有些

再议js的传递和深复制

病理 基本类型的传递就是按值传递,比如说 var a = 1; var b = a; b = 3; console.log(a,b);//1,3 很明显,a的值并未因为b的值改变而变化,这是因为a只是给了b一个副本.在这就不详细描述了.下面才是重点 let obj = {x:1}; let o = obj; o.x = 2; console.log(obj.x);//2 已经被改动了 对此我在前面的 js函数参数的传递 中有具体介绍,在这就不多说了. 对症下药 毛主席说过,遇事莫要急,碰到问题就