pt-heartbeat
通过心跳表实测复制数据来监控 MySQL/PostgreSQL 的复制延迟,比依赖复制机制自身的状态更可靠。
语法
pt-heartbeat [OPTIONS] [DSN] --update|--monitor|--check|--stop以守护进程方式在主库上更新 test.heartbeat 心跳表:
pt-heartbeat -D test --update -h source-server --daemonize在从库上持续监控复制延迟:
pt-heartbeat -D test --monitor -h replica-server同一命令也可监控 PostgreSQL(通过 --dbi-driver):
pt-heartbeat -D test --monitor -h replica-server --dbi-driver Pg在从库上检查一次延迟即退出(可用 DSN 指定从库主机):
pt-heartbeat -D test --check h=replica-server用法示例
以下命令假定已通过选项文件或本机 socket 配置好 MySQL 连接;远程主机用 -h主机 或 --defaults-file=/path/my.cnf 指定。命令可直接复制,替换其中的库名与主机即可。
场景:首次部署持续写心跳
在主库上后台常驻一个 --update 实例,首次加 --create-table 让工具自动建心跳表并插入心跳行,之后每隔 --interval(默认 1 秒)更新一次时间戳:
pt-heartbeat --create-table -D test --update -h source-server --daemonize场景:在从库实时看延迟
在从库上跑 --monitor,每秒检查一次并把当前延迟与移动平均打印到 STDOUT,用于实时盯延迟:
pt-heartbeat -D test --monitor -h replica-server场景:脚本里查一次延迟
监控脚本或 Zabbix 取数只想要一个数值,用 --check 打印一次延迟即退出:
pt-heartbeat -D test --check h=replica-server场景:查二级从库的延迟
层级为 source(id=1) -> replica1(id=2) -> replica2 时,从 replica2 算它相对源 server_id=1 的延迟:
pt-heartbeat -D test --source-server-id 1 --check h=replica2场景:守护进程把延迟写文件
配合 --daemonize 把最新延迟写入文件(每间隔截断只留最近一次),便于别的程序读取或绘图:
pt-heartbeat -D test --monitor -h replica-server --file /var/log/pt-heartbeat.log --daemonize场景:跨时区避免假延迟
机器跨时区或时钟不准时,所有实例都加 --utc(建议写进 --config),否则会因时区算出假阳性延迟:
pt-heartbeat --utc -D test --update -h source-server --daemonize场景:不确定表是否已有行用 REPLACE
--update 默认用 UPDATE,心跳表没行时会失败;加 --replace 改用 REPLACE 语句,无论表是否已有行都能写入时间戳:
pt-heartbeat -D test --update --replace -h source-server功能说明
pt-heartbeat 是一个两部分的复制延迟监控系统:第一部分(--update)连接到复制源(主库),每隔 --interval 秒更新一次心跳表里的 timestamp("心跳记录");第二部分(--monitor 或 --check)连接到从库,读取被复制过来的心跳记录,用当前系统时间减去记录中的时间,得到复制延迟。
之所以不用 Seconds_Behind_Master(即 SHOW REPLICA STATUS 的 Seconds_Behind_Source)来度量延迟,是因为它依赖复制机制自身,并不可靠。pt-heartbeat 只依赖于心跳记录被真正复制到了从库——所以无论是内建复制、还是 Continuent Tungsten 之类的系统都能工作;在复制层级的任意深度都准确(能可靠报告从库落后其"源之源"多少);即便复制停了,它仍会如实报告从库在持续掉队。
心跳表与工作原理
必须手动在主库上创建心跳表,或用 --create-table 让工具创建。建表结构如下(即 --create-table 使用的 MAGIC_create_heartbeat 定义):
CREATE TABLE heartbeat (
ts varchar(26) NOT NULL,
server_id int unsigned NOT NULL PRIMARY KEY,
file varchar(255) DEFAULT NULL, -- SHOW BINARY LOG STATUS
position bigint unsigned DEFAULT NULL, -- SHOW BINARY LOG STATUS
relay_source_log_file varchar(255) DEFAULT NULL, -- SHOW REPLICA STATUS
exec_source_log_pos bigint unsigned DEFAULT NULL -- SHOW REPLICA STATUS
);心跳表至少要有一行。手动建表后需插入一行:
INSERT INTO heartbeat (ts, server_id) VALUES (NOW(), N);使用 --utc 时则插入 UTC_TIMESTAMP() 而非 NOW():
INSERT INTO heartbeat (ts, server_id) VALUES (UTC_TIMESTAMP(), N);其中 N 是 server 的 ID;不要使用 @@server_id,因为它会被复制,从库会插入自己的 server ID 而非源库的。这一行由 --create-table 自动插入。默认情况下,工具发现表没有心跳行时会自动插入(--[no]insert-heartbeat-row 控制);若数据库用户没有 INSERT 权限,用 --no-insert-heartbeat-row 关闭该特性。
旧版心跳表结构(仍受支持,但 ts 为 datetime 类型,最大精度只有 1 秒)如下:
CREATE TABLE heartbeat (
id int NOT NULL PRIMARY KEY,
ts datetime NOT NULL
);旧版表不支持多级复制层级(如 source -> replica1 -> replica2)里每个节点都跑 --update;手动插入必需行用 INSERT INTO heartbeat (id, ts) VALUES (1, NOW());(或 --utc 时用 UTC_TIMESTAMP())。工具会自动检测表是否为旧版。建议 MySQL 用 MEMORY 引擎(非必须)。
多级复制层级
若复制层级含多个"既是源又是从"的节点(如 source -> replica1 -> replica2),可在源和从库上都跑 --update 实例。默认心跳表以 server_id 列为主键,每个 server 更新 server_id=@@server_id 的那一行。
对 --monitor/--check,若未指定 --source-server-id,工具会尝试发现并使用从库的直连源;若失败或想监控来自其它源的延迟,可显式指定 --source-server-id。例如层级 source -> replica1 -> replica2 对应 server ID 1、2、3:
pt-heartbeat --daemonize -D test --update -h source
pt-heartbeat --daemonize -D test --update -h replica1随后检查从 source 到 replica2 的延迟:
pt-heartbeat -D test --source-server-id 1 --check replica2或检查从 replica1 到 replica2 的延迟:
pt-heartbeat -D test --source-server-id 2 --check replica2停掉 replica1 上的 --update 实例不会影响 source 上的实例。默认心跳表还留有保存 SHOW BINARY LOG STATUS(SHOW MASTER STATUS,MySQL 8.4 之前)与 SHOW REPLICA STATUS 信息的列,这些列可选;若存在则写入相应信息。
时钟与精度
pt-heartbeat 最大分辨率为 0.01 秒。源库与从库的时钟必须通过 NTP 紧密同步。默认 --update 在秒的边界打心跳(如 00:01),--monitor 在秒中央检查(如 00:01.5)。只要时钟同步、复制事件在半秒内传播,工具就报零延迟。若连接出错会尝试重连,但启动时连不上不会重试。
与 Percona XtraDB Cluster
pt-heartbeat 应能在所有受支持的 PXC 版本上工作,但建议用 5.5.28-23.7 及以上。在集群节点间部署心跳实例时,由于集群速度取决于最慢节点,它只报告事件从一个节点复制到另一个节点的速度,而非集群自身多快。对 --monitor/--check 必须指定 --source-server-id。
输出
--monitor 模式下每行输出形如:
5s [ 0.25s, 0.05s, 0.02s ]- 第一个数字(如
5s)是当前测得的延迟(current delay)。 - 方括号内是
--frames指定各时间窗口上的移动平均值,依次对应1m,5m,15m(默认)。最大窗口决定内存占用,因为最多会保留该窗口内每个整秒的样本。
--check 模式只输出一次延迟(或配合 --recurse 输出各从库的延迟,前面打印主机/IP 与端口)。若同时指定 --print-source-server-id,每行末尾会打印自动发现或给定的 --source-server-id。
选项
| 选项 | 说明 |
|---|---|
--ask-pass | 连接 MySQL 时交互式询问密码 |
--[no]buffer-stdout | 默认开启 STDOUT 缓冲;使用 tee、kubectl logs 等后处理工具想看到实时进度时可关闭 |
--charset | 类型:string。默认字符集(utf8 时设置 binmode/mysql_enable_utf8 并执行 SET NAMES UTF8) |
--check | 检查一次从库延迟即退出;若同时指定 --recurse,会尝试发现该从库的下级从库并检查、打印其延迟(延迟前打印主机/IP 与端口);--recurse 仅支持 MySQL |
--check-read-only | 检查服务器是否开启 read_only;若开启,工具跳过任何插入。参见 --read-only-interval |
--config | 类型:Array。读取逗号分隔的配置文件列表;如指定必须放在命令行第一个选项的位置 |
--create-table | 若心跳 --table 不存在则创建(使用 MAGIC_create_heartbeat 定义)。表仍需至少一行(自动插入);手动建表时记得插入行 |
--create-table-engine | 类型:string。心跳表使用的存储引擎;MySQL 5.5.5 起默认为 InnoDB |
--daemonize | fork 到后台并从 shell 脱离(仅 POSIX 系统) |
-D, --database | 类型:string。连接使用的默认数据库 |
--dbi-driver | 默认:mysql;类型:string。连接驱动;支持 mysql 和 Pg,可据此前者监控 MySQL、后者监控 PostgreSQL(与 Slony-1 复制配合良好) |
-F, --defaults-file | 类型:string。只从给定文件读取 mysql 选项,必须给绝对路径 |
--fail-successive-errors | 类型:int。连续出现指定次数的 DBI 错误(连接不上或执行查询失败)后,工具退出 |
--file | 类型:string。给定 --monitor 时,把最新输出写入该文件而非 STDOUT;文件每个间隔被打开、截断、关闭,只保留最近一次统计。与 --daemonize 配合有用 |
--frames | 类型:string;默认:1m,5m,15m。--monitor 时计算移动平均值的时间窗口;逗号分隔、后缀 s/m/h/d。最大窗口决定内存占用(保留该窗口内每整秒样本);窗口数量不限 |
--help | 显示帮助并退出 |
-h, --host | 类型:string。要连接的主机 |
--[no]insert-heartbeat-row | 默认:yes。若 --table 中无心跳行则插入一行;表没有行就没有可 --update/--monitor/--check 的对象。用户无 INSERT 权限时用 --no-insert-heartbeat-row 关闭 |
--interval | 类型:float;默认:1.0。更新或检查心跳表的频率(秒)。更新/检查从下一个整秒开始,之后每 --interval 秒重复(--monitor 为 --interval+--skew)。最小 0.01,最多两位小数(0.015 舍入为 0.02);旧版心跳表精度为 1 秒 |
--log | 类型:string。守护进程化时把所有输出写入该文件 |
--master-server-id | 类型:string。已废弃,将来版本移除;改用 --source-server-id |
--source-server-id | 类型:string。对 --monitor/--check 从该源 server ID 计算延迟;未给时尝试连接服务器的源以确定其 ID |
--monitor | 持续监控从库延迟:每秒检查一次并向 STDOUT(或 --file)报告当前延迟及 --frames 各窗口的移动平均值 |
--mysql_ssl | 类型:int。创建 SSL MySQL 连接 |
-p, --password | 类型:string。连接密码(含逗号需转义) |
--pid | 类型:string。创建指定的 PID 文件;冲突规则与自动清理见官方文档 |
-P, --port | 类型:int。连接端口 |
--print-master-server-id | 已废弃,将来版本移除;改用 --print-source-server-id |
--print-source-server-id | 打印自动发现或给定的 --source-server-id;若指定了 --check/--monitor,每行末尾打印该值 |
--read-only-interval | 类型:int。指定 --check-read-only 时,服务器处于只读状态的睡眠间隔;未指定则用 --interval |
--recurse | 类型:int。在 --check 模式下按此深度递归检查从库(仅 MySQL);发现从库后逐个检查并打印主机(若可得)与延迟。参见 --recursion-method |
--recursion-method | 类型:array;默认:processlist,hosts。发现从库的首选方法:processlist(SHOW PROCESSLIST)、hosts(SHOW REPLICA HOSTS)、none(不发现)。processlist 更可靠;使用非标准端口(非 3306)时需 hosts |
--replace | 在 --update 模式下用 REPLACE 而非 UPDATE 设置时间戳(REPLACE 是 MySQL 扩展);不确定表是否有行时有用。必须与 --update 一起使用 |
--run-time | 类型:time。运行该时长后退出 |
--sentinel | 类型:string;默认:/tmp/pt-heartbeat-sentinel。该文件存在则退出 |
--slave-password | 已废弃,将来版本移除;改用 --replica-password |
--slave-user | 已废弃,将来版本移除;改用 --replica-user |
--replica-user | 类型:string。连接从库所用的用户;该用户必须存在于所有从库(可权限较低) |
--replica-password | 类型:string。连接从库所用的密码;与 --replica-user 配合,且所有从库上密码须一致 |
--set-vars | 类型:Array。以逗号分隔的 变量=值 列表设置 MySQL 变量;默认 wait_timeout=10000;无法设置时打印警告并继续 |
--skew | 类型:float;默认:0.5。检查延后多久;默认延后半秒,使源库在秒初立即打心跳后留有 0.5 秒复制余量才报 1 秒延迟。时钟不准时可调 |
-S, --socket | 类型:string。连接使用的 socket 文件 |
--stop | 通过创建 sentinel 文件停止运行中的实例:停止所有监视同一 sentinel 文件的实例;若未指定 --update/--monitor/--check 则在建文件后退出,若指定则等待 --interval 后删除文件并继续工作。便于优雅停 cron 或替换实例 |
--table | 类型:string;默认:heartbeat。心跳表名;不要写 database.table,用 --database 指定库。参见 --create-table |
--update | 更新复制源的心跳(每隔 --interval 写一次时间戳) |
-u, --user | 类型:string。登录用户(若非当前用户) |
--utc | 忽略系统时区、只用 UTC。默认不校正时区会导致延迟算错,建议开启且所有实例(--update/--monitor/--check 等)都用;混用会造成时区导致的假阳性延迟。可写入 --config |
--version | 显示版本并退出 |
--[no]version-check | 默认:yes。检查 Percona Toolkit、MySQL 等软件的最新版本与已知问题版本(详见版本检查) |
说明:
--update、--monitor、--check互斥;--daemonize与--check互斥;必须至少指定--stop、--update、--monitor、--check之一。
用每小时重启来确保 pt-heartbeat 一直在跑(停掉并重启旧实例)的 crontab 写法:
0 * * * * pt-heartbeat --update -D test --stop \
--sentinel /tmp/pt-heartbeat-hourly非默认的 --sentinel 确保该 cron 只停掉此前用相同选项(即同一 cron)启动的实例。
DSN 选项
| 键 | DSN 部分 | 说明 |
|---|---|---|
A | charset | 默认字符集 |
D | database | 默认数据库 |
F | mysql_read_default_file | 只从给定文件读取默认选项 |
h | host | 要连接的主机 |
p | password | 连接密码(含逗号需转义) |
P | port | 连接端口 |
S | mysql_socket | 连接使用的 socket 文件 |
u | user | 登录用户(若非当前用户) |
s | mysql_ssl | 创建 SSL 连接 |
其他信息
作者:Proven Scaling LLC、SixApart Ltd、Baron Schwartz 和 Daniel Nichter
通用说明:已知问题的反馈方式、PTDEBUG 调试安全提示、系统要求基线,见 通用说明。
更多细节请阅读 官方文档。