检测MySQL主从延迟,通常使用MySQL自带的Seconds_Behind_Master,但是这个状态变量反映的是秒级别的复制延迟,无法做到更精确的延迟检测。此外,服务器时间不同步也会导致这个状态变量不能真实反映主从间的复制延迟。下面介绍一个更理想的MySQL主从复制延迟检测工具pt-heartbeat。
pt-heartbeat原理:
pt-heartbeat需要在数据库中创建一张表,表中有一个时间戳字段,每隔一段时间去更新这个时间戳,通过对比从库的这条记录时间戳与当前系统时间,来获得MySQL主从延迟的大小。表结构如下:
CREATE TABLE `heartbeat` (
`ts` varchar(26) NOT NULL,
`server_id` int(10) unsigned NOT NULL,
`file` varchar(255) DEFAULT NULL,
`position` bigint(20) unsigned DEFAULT NULL,
`relay_master_log_file` varchar(255) DEFAULT NULL,
`exec_master_log_pos` bigint(20) unsigned DEFAULT NULL,
PRIMARY KEY (`server_id`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4;
pt-heartbeat使用:
版本:
pt-heartbeat 3.1.0
MySQL 5.7.19
在MySQL主从集群中创建pt-heartbeat用的数据库和账号密码。
在主库创建heartbeat表,并在后台定时更新heartbeat表记录:
pt-heartbeat -D pt_heartbeat -h master_ip -uxxx -p'xxx' --update --daemonize --create-table
检测从库复制延迟:
pt-heartbeat -D pt_heartbeat -h slave_ip -uxxx -p'xxx' --master-server-id=1 --monitor
输出结果如下:
0.00s [ 0.00s, 0.00s, 0.00s ]
0.00s [ 0.00s, 0.00s, 0.00s ]
0.00s [ 0.00s, 0.00s, 0.00s ]
0.00s [ 0.00s, 0.00s, 0.00s ]
0.00s [ 0.00s, 0.00s, 0.00s ]
0.00s [ 0.00s, 0.00s, 0.00s ]
0.00s [ 0.00s, 0.00s, 0.00s ]
0.00s [ 0.00s, 0.00s, 0.00s ]
0.00s [ 0.00s, 0.00s, 0.00s ]
0.00s [ 0.00s, 0.00s, 0.00s ]
0.00s [ 0.00s, 0.00s, 0.00s ]
0.00s [ 0.00s, 0.00s, 0.00s ]
0.00s [ 0.00s, 0.00s, 0.00s ]
0.00s [ 0.00s, 0.00s, 0.00s ]
第一列表示当前最新的复制延迟时间,[ 0.00s, 0.00s, 0.00s ]表示1min,5min,15min的平均延迟时间。
pt-heartbeat问题:
复制延迟显示精度问题,默认情况下延迟的最小值为10ms,小于10ms的延迟都显示为0。pt-heartbeat工具也没有参数来调整显示精度。
输出结果无法重定向,虽然可以指定参数--file 将结果打印到文件中,但是每个间隔,都会覆盖上一次的结果,无法保留历史。
通过修改pt-heartbeat源码/usr/bin/pt-heartbeat,解决以上2个问题。
显示精度问题修改代码行6310和6314,如下:
# 2.00s [ 0.05s, 0.01s, 0.00s ]
my $format = ($hires_ts ? '%1$.6f' : '%1$4d') . "s [ ";
my $findex = 2;
foreach (@$frames) {
$format .= $findex > 2 ? ', ' : '';
$format .= '%'.$findex.'$5.6fs';
$findex++;
}
$format .= " ]";
$format .= ($o->get('print-master-server-id') ? ' %'.$findex.'$d' : '')
. "
";
--file参数结果覆盖问题,修改代码行6387, > 改为 >> ,如下:
my $output = sprintf $format, $delay, @vals, $pk_val;
if ( my $file = $o->get('file') ) {
open my $file, '>>', $file
or die "Can't open $file: $OS_ERROR";
print $file $output
or die "Can't print to $file: $OS_ERROR";
close $file
or die "Can't close $file: $OS_ERROR";
}
else {
print $output;
}