Skip to content

pt-heartbeat

通过心跳表实测复制数据来监控 MySQL/PostgreSQL 的复制延迟,比依赖复制机制自身的状态更可靠。

语法

bash
pt-heartbeat [OPTIONS] [DSN] --update|--monitor|--check|--stop

以守护进程方式在主库上更新 test.heartbeat 心跳表:

bash
pt-heartbeat -D test --update -h source-server --daemonize

在从库上持续监控复制延迟:

bash
pt-heartbeat -D test --monitor -h replica-server

同一命令也可监控 PostgreSQL(通过 --dbi-driver):

bash
pt-heartbeat -D test --monitor -h replica-server --dbi-driver Pg

在从库上检查一次延迟即退出(可用 DSN 指定从库主机):

bash
pt-heartbeat -D test --check h=replica-server

用法示例

以下命令假定已通过选项文件或本机 socket 配置好 MySQL 连接;远程主机用 -h主机--defaults-file=/path/my.cnf 指定。命令可直接复制,替换其中的库名与主机即可。

场景:首次部署持续写心跳

在主库上后台常驻一个 --update 实例,首次加 --create-table 让工具自动建心跳表并插入心跳行,之后每隔 --interval(默认 1 秒)更新一次时间戳:

bash
pt-heartbeat --create-table -D test --update -h source-server --daemonize

场景:在从库实时看延迟

在从库上跑 --monitor,每秒检查一次并把当前延迟与移动平均打印到 STDOUT,用于实时盯延迟:

bash
pt-heartbeat -D test --monitor -h replica-server

场景:脚本里查一次延迟

监控脚本或 Zabbix 取数只想要一个数值,用 --check 打印一次延迟即退出:

bash
pt-heartbeat -D test --check h=replica-server

场景:查二级从库的延迟

层级为 source(id=1) -> replica1(id=2) -> replica2 时,从 replica2 算它相对源 server_id=1 的延迟:

bash
pt-heartbeat -D test --source-server-id 1 --check h=replica2

场景:守护进程把延迟写文件

配合 --daemonize 把最新延迟写入文件(每间隔截断只留最近一次),便于别的程序读取或绘图:

bash
pt-heartbeat -D test --monitor -h replica-server --file /var/log/pt-heartbeat.log --daemonize

场景:跨时区避免假延迟

机器跨时区或时钟不准时,所有实例都加 --utc(建议写进 --config),否则会因时区算出假阳性延迟:

bash
pt-heartbeat --utc -D test --update -h source-server --daemonize

场景:不确定表是否已有行用 REPLACE

--update 默认用 UPDATE,心跳表没行时会失败;加 --replace 改用 REPLACE 语句,无论表是否已有行都能写入时间戳:

bash
pt-heartbeat -D test --update --replace -h source-server

功能说明

pt-heartbeat 是一个两部分的复制延迟监控系统:第一部分(--update)连接到复制源(主库),每隔 --interval 秒更新一次心跳表里的 timestamp("心跳记录");第二部分(--monitor--check)连接到从库,读取被复制过来的心跳记录,用当前系统时间减去记录中的时间,得到复制延迟。

之所以不用 Seconds_Behind_Master(即 SHOW REPLICA STATUSSeconds_Behind_Source)来度量延迟,是因为它依赖复制机制自身,并不可靠。pt-heartbeat 只依赖于心跳记录被真正复制到了从库——所以无论是内建复制、还是 Continuent Tungsten 之类的系统都能工作;在复制层级的任意深度都准确(能可靠报告从库落后其"源之源"多少);即便复制停了,它仍会如实报告从库在持续掉队。

心跳表与工作原理

必须手动在主库上创建心跳表,或用 --create-table 让工具创建。建表结构如下(即 --create-table 使用的 MAGIC_create_heartbeat 定义):

sql
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
);

心跳表至少要有一行。手动建表后需插入一行:

sql
INSERT INTO heartbeat (ts, server_id) VALUES (NOW(), N);

使用 --utc 时则插入 UTC_TIMESTAMP() 而非 NOW()

sql
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 关闭该特性。

旧版心跳表结构(仍受支持,但 tsdatetime 类型,最大精度只有 1 秒)如下:

sql
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:

bash
pt-heartbeat --daemonize -D test --update -h source
pt-heartbeat --daemonize -D test --update -h replica1

随后检查从 source 到 replica2 的延迟:

bash
pt-heartbeat -D test --source-server-id 1 --check replica2

或检查从 replica1 到 replica2 的延迟:

bash
pt-heartbeat -D test --source-server-id 2 --check replica2

停掉 replica1 上的 --update 实例不会影响 source 上的实例。默认心跳表还留有保存 SHOW BINARY LOG STATUSSHOW 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
--daemonizefork 到后台并从 shell 脱离(仅 POSIX 系统)
-D, --database类型:string。连接使用的默认数据库
--dbi-driver默认:mysql;类型:string。连接驱动;支持 mysqlPg,可据此前者监控 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 写法:

bash
0 * * * * pt-heartbeat --update -D test --stop \
  --sentinel /tmp/pt-heartbeat-hourly

非默认的 --sentinel 确保该 cron 只停掉此前用相同选项(即同一 cron)启动的实例。

DSN 选项

DSN 部分说明
Acharset默认字符集
Ddatabase默认数据库
Fmysql_read_default_file只从给定文件读取默认选项
hhost要连接的主机
ppassword连接密码(含逗号需转义)
Pport连接端口
Smysql_socket连接使用的 socket 文件
uuser登录用户(若非当前用户)
smysql_ssl创建 SSL 连接

其他信息

  • 作者:Proven Scaling LLC、SixApart Ltd、Baron Schwartz 和 Daniel Nichter

  • 通用说明:已知问题的反馈方式、PTDEBUG 调试安全提示、系统要求基线,见 通用说明

更多细节请阅读 官方文档

Percona Toolkit 中文文档 · 社区维护的第三方学习站