日常工作中,对于MySQL主从复制检查,一方面我们要保证复制的整体结构是否正常,另一方面需要检查主从数据是否保持一致。对于前者我们可以 通过监控复制线程是否工作正常以及主从延时是否在容忍范围内,对于后者则可以通过分别校验主从表中数据的md5码是否一致,来保证数据一致,可以使用Maatkit工具包中的mk-table-checksum工具去检查。在这里,我只想讨论下关于如何检查主从延时的问题。
判断主从延时,通常有两个方法:1. Seconds_Behind_Master vs 2. mk-heartbeat,下面具体说下两者在实现功能的差别。
方法1. 通过监控show slave statusG命令输出的Seconds_Behind_Master参数的值来判断,是否有发生主从延时。其值有这么几种:
NULL — 表示io_thread或是sql_thread有任何一个发生故障,也就是该线程的Running状态是No,而非Yes。
0 — 该值为零,是我们极为渴望看到的情况,表示主从复制良好,可以认为lag不存在。
正值 — 表示主从已经出现延时,数字越大表示从库落后主库越多。
负值 — 几乎很少见,我只是听一些资深的DBA说见过,其实,这是一个BUG值,该参数是不支持负值的,也就是不应该出现。
show slave statusG,该命令的输出结果非常丰厚,给我们的监控提供了很多有意义的参数,比如:Slave_IO_Running该参数可作为 io_thread的监控项,Yes表示io_thread的和主库连接正常并能实施复制工作,No则说明与主库通讯异常,多数情况是由主从间网络引起的 问题;Slave_SQL_Running该参数代表sql_thread是否正常,具体就是语句是否执行通过,常会遇到主键重复或是某个表不存在。下面 就说到今天的重点Seconds_Behind_Master,该值作为判断主从延时的指标,那么它又是怎么得到这个值的呢,同时,它为什么又受到很多人 的质疑?
Seconds_Behind_Master是通过比较sql_thread执行的event的timestamp和io_thread复制好的 event的timestamp(简写为ts)进行比较,而得到的这么一个差值。我们都知道的relay-log和主库的bin-log里面的内容完全一 样,在记录sql语句的同时会被记录上当时的ts,所以比较参考的值来自于binlog,其实主从没有必要与NTP进行同步,也就是说无需保证主从时钟的 一致。你也会发现,其实比较真正是发生在io_thread与sql_thread之间,而io_thread才真正与主库有关联,于是,问题就出来了, 当主库I/O负载很大或是网络阻塞,io_thread不能及时复制binlog(没有中断,也在复制),而sql_thread一直都能跟上 io_thread的脚本,这时Seconds_Behind_Master的值是0,也就是我们认为的无延时,但是,实际上不是,你懂得。这也就是为什 么大家要批判用这个参数来监控数据库是否发生延时不准的原因,但是这个值并不是总是不准,如果当io_thread与master网络很好的情况下,那么 该值也是很有价值的。(就好比:妈–儿子–媳妇的关系,妈与儿子亲人,媳妇和儿子也亲人,不见得媳妇与妈就很亲。开个玩笑:-)之前,提到 Seconds_Behind_Master这个参数会有负值出现,我们已经知道该值是io_thread的最近跟新的ts与sql_thread执行到 的ts差值,前者始终是大于后者的,唯一的肯能就是某个event的ts发生了错误,比之前的小了,那么当这种情况发生时,负值出现就成为可能。
方法2. mk-heartbeat,Maatkit万能工具包中的一个工具,被认为可以准确判断复制延时的方法。
mk-heartbeat的实现也是借助timestmp的比较实现的,它首先需要保证主从服务器必须要保持一致,通过与相同的一个NTP server同步时钟。它需要在主库上创建一个heartbeat的表,里面至少有id与ts两个字段,id为server_id,ts就是当前的时间戳 now(),该结构也会被复制到从库上,表建好以后,会在主库上以后台进程的模式去执行一行更新操作的命令,定期去向表中的插入数据,这个周期默认为1 秒,同时从库也会在后台执行一个监控命令,与主库保持一致的周期去比较,复制过来记录的ts值与主库上的同一条ts值,差值为0表示无延时,差值越大表示 延时的秒数越多。我们都知道复制是异步的ts不肯完全一致,所以该工具允许半秒的差距,在这之内的差异都可忽略认为无延时。这个工具就是通过实打实的复 制,巧妙的借用timestamp来检查延时,赞一个!
以上就是本人对这两个方法的理解,在这之前也和部落的lulu还有公司的田老师交流过,也希望大家来说说你们的理解。
说下个人的理解:
seconds_behinds_master ~= slave I/O thread time - slave SQL thread time
slave I/O thread time并不是 I/O thread上的timestamp,而是slave I/O thread在和master同步binlog的过程中记录的master的当前时间,也就是,slave I/O thread time等于I/O thread最近一次和master交互时的时间
slave SQL thread time是SQL线程在salve上执行binlog时,在binlog里记录的时间,这个时间是master在生成binlog时的时间,也就是mater上的时间
所以他们的差值一般情况下可以衡量主从同步延迟,在下面两种情况下出现异常:
1. 主从之间的网络延迟较大,导致slave I/O thread与master的交互延后,自然slave I/O thread time得不到更新,所以它不是最新的时间,而在这个时候SQL I/O thread不断的执行binlog中的SQL,自然就会不断的追上slave I/O thread的步伐,所以seconds_behind_master的值接近0,但其实是存在延迟的
2. 有很长一段时间没有数据提交,slave I/O thread time和slave SQL thread time都保持在旧值,比如A(但事实上master上的时间已经到A+I了),这个时候主库出现提交,slave I/O开始去和master同步binlog,slave I/O thread time更新到A+I,但是slave SQL thread time保持在A值,这时的seconds_behind_master=I,出现较大延迟,但其实是否出现延迟是不确定的
下面还有其他人的说法:
Mysql主从同步延迟受到多种因素影响, 比如大事务, 从库查询压力, 网路延迟等; 这些比较常见; 但还受到主从机器系统时钟差的影响,这一点可能容易被忽视。
上周, 就遇到了这样的情况, 主库的系统时间由于某种原因落后于从库几十秒, 结果频繁的出现大的主从延迟同步 ,查了N久业务方面的问题, 都找不出原因; 在和同事的交流中,发现大家对参数Seconds_Behind_Master的理解有点补一样,基本有两种理解:
- 一种理解是来源于Mysql手册上的描述, 大体意思是这个时间是从库SQL线程处理的最近的日志事件的时间戳减去从库IO线程处理的最近一条日志记录的时间戳得到的, 可以简单理解为从库SQL线程与IO线程所处理的最近的日志事件的时间戳差;这个计算方式给人的感觉不是在计算主从延迟,而是在计算从库上两个线程的处理 的日志的时差。
- 另一种理解来源于《High Performace Mysql》上的的描述, 大体意思这个参数反映的结果是当前系统时间减去从库IO线程所处理的最近一条日志记录的时间戳; 但这个说法有一个明显的不太让人信服的地方, 就是如果机器的系统时间相差比较大怎么办? 显然, 如果系统时间相差比较大的话, 以这样的方式计算主从延迟毫无意义。
在有分歧的情况下, 去查看了一下Mysql的源代码, 结果发现手册上的描述居然不那么准确, 代码大致如下:
......
if ((mi->slave_running == MYSQL_SLAVE_RUN_CONNECT) &&
mi->rli.slave_running)
{
long time_diff= (( long)(time(0) - mi->rli.last_master_timestamp)
- mi->clock_diff_with_master); -------time(0)表示slave的当前时间,用它减去slave和master的时间差值,再减去最近执行的binlog日志的时间(该时间由master产生), 如果使用slave的时间戳time(0),如果一段时间没有binlog日志产生,second_behind_master将不准确, 因为last_master_timestap并未更新
protocol->store((longlong)(mi->rli.last_master_timestamp ?
max(0, time_diff) : 0));
......
}
else
{
protocol->store_null();
}
从代码看, 如果从库IO线程到主库的连接有问题或者SQL线程没有在运行, Seconds_Behind_Master直接返回NULL; 否则的话, 用从库当前系统时间减去IO线程处理的最近的事件的时间戳; 代码里用mi->clock_diff_with_master来排除系统时间差对计算的影响, 那这个值又是怎么计算来的呢? 继续看代码:
if (!mysql_real_query(mysql, STRING_WITH_LEN(" SELECT UNIX_TIMESTAMP()")) &&
(master_res= mysql_store_result(mysql)) &&
(master_row= mysql_fetch_row(master_res)))
{
mi->clock_diff_with_master = ( long) (time((time_t*) 0) - strtoul(master_row[0], 0, 10));
}
else if (!check_io_slave_killed(mi->io_thd, mi, NULL))
{
mi->clock_diff_with_master= 0; /* The "most sensible" value */
......
}
原来这个值是通过在主库上执行SELECT UNIX_TIMESTAMP()来取得主库的系统时间, 然后去减从库的当前系统时间。
原来系统时间差还真的对主从同步延迟参数Seconds_Behind_Master有影响。-----系统时间为什么影响该值?
别人对于seconds_behind_master的解释:
Read_Master_Log_Pos和Exec_Master_Log_Pos位置之间的时间差值吧.这个值只能在一定程度上说明复制良好,但是经常 不准确,比如主从之间网络环境很差的时候,可能主和从的数据差异很大,但是复制过来的日志都能被slave的sql线程执行完 成,seconds_behind_master此时为0。可以通过在主上show master statusG的Position,和从执行show slave statusG的 Read_Master_Log_Pos和Exec_Master_Log_Pos进行比较得出复制是否能跟上主的状态。
又重新想了下这个问题,有这样一个回复,我认为应该是标准答案了:
根据relaylog中读到的最后一条binlog的 timestamp 和 sql thread 执行的最新一条binlog timestamp的差值决定,具体见文档
为什么使用binlog的日志而不是系统时间,我认为是为了保证主从机器系统时间不一致的时候也能得到正确的结果
转自:http://blog.chinaunix.net/uid-20466636-id-3037462.html