MySQL · 2016-10-19 1

Percona-Toolkit系列之pt-heartbeat主备延时监控利器

1.mysql主从延迟

对于MySQL原生主从复制,可以毫不夸张的说,它是大多数MySQL高可用方案的基础,MHA,MMM等等都是基于原生复制。但是mysql原生复制一直以来有个诟病就是复制延时,这是一个困扰MySQL DBA多年的问题,官方提出了并行复制的解决方案,业界也搞出了galera cluster来解决延时问题()。

关于主备延时,怎么理解?问十个人有十个人的理解,并没有一个既定的俗成的定义给主备延时。首先我们来看看官网文档的解释:

Seconds_Behind_Master: The number of seconds that the slave SQL thread is behind processing
the master binary log. A high number (or an increasing one) can indicate that the slave is unable to
handle events from the master in a timely fashion

翻译过来大概意思就是从库的SQL线程来不及执行主库发送过来的binlog。mysql主备环境下,主备同步过程如下,主库更新产生binlog, 备库io线程获取主库binlog在备库生成relay log。备库sql线程执行relay log从而保持和主库同步。理论上讲主库有更新时,备库都存在延迟,且延迟时间为备库执行时间+网络传输时间。一般主从会做读写分离,如果你一点也不能接受延迟带来的影响,那么请到主库读取,从库不可能实时百分子百追上主库,这个延时可大可小,多少都是有的。

下面我们来看一个真是的主从延时例子,我首先停掉sql_thread,然后断开io_thread。

主库:

root@localhost [(none)] 03:45:42>>>show master status \G   
*************************** 1. row ***************************
             File: mysql-bin.000045
         Position: 18944892
     Binlog_Do_DB: 
 Binlog_Ignore_DB: 
Executed_Gtid_Set: 3cbbee25-827c-11e6-b11b-0800276f0c66:1-64429,
f95dd499-827c-11e6-aaea-080027e92059:1
1 row in set (0.00 sec)

备库:

root@localhost [(none)] 03:45:46>>>show slave status\G
*************************** 1. row ***************************
               Slave_IO_State: 
                  Master_Host: 192.168.56.103
                  Master_User: repl
                  Master_Port: 3306
                Connect_Retry: 60
              Master_Log_File: mysql-bin.000045
          Read_Master_Log_Pos: 9472697
               Relay_Log_File: mysql-relay-bin.000002
                Relay_Log_Pos: 675
        Relay_Master_Log_File: mysql-bin.000045
             Slave_IO_Running: No
            Slave_SQL_Running: No
              Replicate_Do_DB: 
          Replicate_Ignore_DB: 
           Replicate_Do_Table: 
       Replicate_Ignore_Table: 
      Replicate_Wild_Do_Table: 
  Replicate_Wild_Ignore_Table: 
                   Last_Errno: 0
                   Last_Error: 
                 Skip_Counter: 0
          Exec_Master_Log_Pos: 502
              Relay_Log_Space: 9473077
              Until_Condition: None
               Until_Log_File: 
                Until_Log_Pos: 0
           Master_SSL_Allowed: No
           Master_SSL_CA_File: 
           Master_SSL_CA_Path: 
              Master_SSL_Cert: 
            Master_SSL_Cipher: 
               Master_SSL_Key: 
        Seconds_Behind_Master: NULL
Master_SSL_Verify_Server_Cert: No
                Last_IO_Errno: 0
                Last_IO_Error: 
               Last_SQL_Errno: 0
               Last_SQL_Error: 
  Replicate_Ignore_Server_Ids: 
             Master_Server_Id: 11
                  Master_UUID: 3cbbee25-827c-11e6-b11b-0800276f0c66
             Master_Info_File: mysql.slave_master_info
                    SQL_Delay: 0
          SQL_Remaining_Delay: NULL
      Slave_SQL_Running_State: 
           Master_Retry_Count: 86400
                  Master_Bind: 
      Last_IO_Error_Timestamp: 
     Last_SQL_Error_Timestamp: 
               Master_SSL_Crl: 
           Master_SSL_Crlpath: 
           Retrieved_Gtid_Set: 3cbbee25-827c-11e6-b11b-0800276f0c66:64227-64328
            Executed_Gtid_Set: 3cbbee25-827c-11e6-b11b-0800276f0c66:1-64227,
f95dd499-827c-11e6-aaea-080027e92059:1
                Auto_Position: 1
         Replicate_Rewrite_DB: 
                 Channel_Name: 
           Master_TLS_Version: 
1 row in set (0.00 sec)

几个重点位置对应关系如下图所示:

nice

那么我们从那里看主备延时呢?首先我们可以从官方原生支持的Seconds_Behind_Master状态量查看。但是我们在生产环境中一般不使用该状态量作为主备延迟参考,主要原因请参考博文(https://yq.aliyun.com/articles/11032),该文从源码层面解释为何Seconds_Behind_Master不是那么可靠。从上面的demo也可以看出,Seconds_Behind_Master居然为null,shit。

2.主流主备延时监控

既然Seconds_Behind_Master不是那么可靠,我们得想个靠谱的方案啊。这时我们可以借鉴下oracle active dataguard主备延时计算的算法,adg的延时计算大概是这么干的,dataguard是基于redo的物理复制,玩过oracle的朋友肯定知道oracle中有个scn(有兴趣可以自行研究下oracle的SCN),scn和时间可以相互转化,这就简单了,主备之间的scn稍微转换下,再一减不就得了,还是很准的哦。因为SCN把数据库当前执行的事务和时间扯到一起了,借着这个思想,我们可以在mysql中模仿出来,不搞那么高大上的SCN,我们只搞最简单的时间戳来模拟。

主要思想如下:

1.在test数据库中定义一张表heartbeat,只含有一列,字段类型为时间戳

2.主库中通过event每隔一秒,更新表heartbeat中的时间戳字段

3.更新操作被放入binlog中

4.备库拉取主库binlog,备库重放binlog,备库中表heartbeat的时间戳字段也会随之更新

5.我们再获取备库当前时间,将当前时间与表T中的时间戳字段相减,得出时间差。

那么binlog中就是这样的,每隔一秒左右(并不百分百准确)就穿插着对表T的更新。这样得出的时间差就是备库当前重放的日志在主库执行时间相对于当前时间的差值,也就是主备延时的差值,比如主库在10:00:01对某表PPP执行了更新操作,备库却在11:00:01才重放该条binlog event,那么主备数据延迟就是一个小时。

pt (1)

 

实现起来也非常简单,首先准备主备环境(略)

一.主库准备

/*主库创建心跳表*/
CREATE TABLE heartbeat (
ts datetime NOT NULL
);
/*存储过程更新心跳表*/
DELIMITER &&  
CREATE  PROCEDURE  update_curr_ts()
BEGIN  
UPDATE heartbeat SET TS=NOW();
END &&  
DELIMITER ; 
/*写个event每秒更新一次心跳表*/
DELIMITER $$
CREATE EVENT IF NOT EXISTS e_blog
ON SCHEDULE EVERY 1 SECOND
ON COMPLETION PRESERVE
DO BEGIN
CALL update_curr_ts();
END$$
DELIMITER ;
/*启用event*/
ALTER EVENT e_blog ON COMPLETION PRESERVE enable;
/*event_scheduler*/
root@localhost [(none)] 06:15:58>>>select @@global.event_scheduler;
+--------------------------+
| @@global.event_scheduler |
+--------------------------+
| ON                       |
+--------------------------+
1 row in set (0.00 sec)

二.备库写个脚本定时查看时差即可

#!/bin/bash
#just for slave
MYSQL_PATH=/u01/app/percona-server-5.7.13-6/bin
MYSQL_HOME=/home/mysql

$MYSQL_PATH/mysql -uroot -p111111 -h192.168.56.103 -P3308 -e "select timestampdiff(SECOND,ts,NOW()) from test.heartbeat;" >$MYSQL_HOME/heartbeat.txt

布上crontab,采集时差

[mysql@hpc02 ~]$ crontab -l
* * * * * sh /home/mysql/script/check_slave.sh

3.pt-heartbeat

好了,扯了这么久了,pt-heartbeat这个猪脚该出场了。pt-heartbeat是什么东东呢,pt-heartbeat所做的事情把上面我的那一大堆工作全部干了,而且还支持pg,是不是很赞。pt-heartbeat的原理和上面我实现的基本一致,只是pt-heartbeat是用的程序定时去更新表中记录。pt-heartbeat包含两部分,第一部分在主库不断更新记录,第二部分在备库不断检测延时。

xtra

原理非常简单,下面我们直接给出常用的配置文件即可。

1.主库配置文件

[mysql@hpc02 ~]$ cat heartbeat_master.cnf 
#DNS#
host=192.168.56.103
user=root
password=111111
port=3306

#GENERAL#
charset=UTF8
check-read-only
create-table
daemonize
interval=1
database=test
table=heartbeat
update
log=/home/mysql/heartbeat_master.log

后台运行

shell> pt-heartbeat –config heartbeat_master.cnf

2.从库配置文件

[mysql@hpc02 ~]$ cat heartbeat_slave.cnf 
#DNS#
host=192.168.56.103
user=root
password=111111
port=3308

#GENERAL#
charset=UTF8
check-read-only
daemonize
file=/home/mysql/heartbeat_monitor.log
frames=1m,10m,30m
log=/home/mysql/heartbeat_slave.log
master-server-id=11
monitor
print-master-server-id
interval=1
database=test

后台运行就好了:

shell> pt-heartbeat –config heartbeat_slave.sh

直接在/home/mysql/heartbeat_monitor.log查看准备延时。