pt-upgrade
对比同一组查询在两个 MySQL 服务器上的执行结果,验证版本升级或配置变更是否兼容。
语法
pt-upgrade [OPTIONS] LOGS|RESULTS DSN [DSN]不要在生产服务器上运行
pt-upgrade 假设两台 MySQL 服务器都是静态、不被改动的(除非用 --no-read-only 由工具自己改)。它没有任何限流选项,会尽可能快地执行查询,是 CPU、内存、磁盘、网络密集型操作。务必只在专用的测试/开发服务器上运行,且两台服务器的环境与数据应保持一致,否则报告会充满误报。
用 slow.log 中的查询把 host2 与 host1 对比:
pt-upgrade h=host1 h=host2 slow.log先把 host1 的参考结果存盘,再用 host2 与存盘结果对比:
pt-upgrade h=host1 --save-results host1_results/ slow.log
pt-upgrade host1_results1/ h=host2用法示例
以下命令假定两台测试服务器的连接信息(账号/密码)已通过选项文件或本机 socket 配好,DSN 里只写 h=主机(必要时加 P=端口)。命令可直接复制,替换其中的主机名、日志路径与阈值即可。pt-upgrade 会真的在两台服务器上执行查询,务必只在静态的测试/开发机上运行,详见上方 warning。
场景:先 dry-run 验证连接与解析
正式跑之前,用 --dry-run 检查两台服务器连通、日志能正常解析、命令行选项无误,但不执行任何查询、不做比较:
pt-upgrade --dry-run h=host1 h=host2 slow.log场景:用 general 日志做升级比对
除了慢日志,写满所有语句的 general 日志同样能当输入;用 --type genlog 指定类型,验证新版本对全量语句的兼容性:
pt-upgrade --type genlog h=old57 h=new80 general.log场景:只比对某个业务库的查询
升级只动到 appdb 时,用 --filter 只保留该库的查询(读不到 db 的事件用 || "" 兜底),避免无关库的差异干扰结论:
pt-upgrade --filter '($event->{db} || "") eq "appdb"' h=host1 h=host2 slow.log场景:限制运行时间,避免大日志跑太久
慢日志很大、只想在限定窗口内比对时,用 --run-time 给个上限(支持单位,如 1h/30m),到点即退出:
pt-upgrade --run-time 1h h=host1 h=host2 slow.log场景:比对写入语句(存储过程/触发器)
升级涉及写操作(如触发器行为差异)时,必须用 --no-read-only 才会执行 INSERT/UPDATE/DELETE;配合 --type binlog 比对重放出的 binlog。这会在两台测试机上都执行写操作,只能在完全隔离、可重建的测试环境里跑,先 --dry-run 确认无误:
mysqlbinlog mysql-bin.000123 > binlog.txt
pt-upgrade --no-read-only --type binlog h=host1 h=host2 binlog.txt场景:调整报告明细粒度
差异太多、报告太长时,用 --max-examples 控制每类差异最多列举几条、--max-class-size 控制每类容纳多少唯一查询,让报告更聚焦:
pt-upgrade --max-examples 5 --max-class-size 500 h=host1 h=host2 slow.log功能说明
用途与两类用法
pt-upgrade 帮助判断把 MySQL 升级(或降级)到新版本是否安全:它把慢日志、general 日志、二进制日志、tcpdump、"raw" 日志中的查询,在两台服务器上分别执行,比较每条查询多方面的执行结果与输出,报告显著差异。两台服务器通常是开发机,一台跑当前生产版本、另一台跑新版本。
它有两类用法:
- 主机对主机(host to host):命令行给一个日志文件加两个 DSN(各对应一台服务器)。查询在运行时于两台服务器执行并即时比较,有差异的查询边跑边打印或结束时打印。不落盘,省硬盘,但再次运行仍需在两台服务器都执行一遍。
- 参考结果对主机(reference results to host):先用
--save-results生成单台服务器的完整参考结果存盘,第二次运行再用另一台服务器与之比较。适合对固定版本做多次比较,或无法同时访问两台服务器时"现在执行、以后比较"。代价是参考结果(含所有行)可能占用大量磁盘。
一致性要求
精确的报告依赖一致的环境与一致的数据。pt-upgrade 不应在生产或任何活跃服务器上运行,因为没有简单办法保证每条查询的读取是同步的;任一台数据在跑的过程中变化,报告里的误报会比真实差异还多。只读(read-only)负载一般不影响工具,最多影响查询耗时,因此可用只读副本。
在主机对主机比较中,第一台主机的查询结果作为"基准",第二台与之比较;参考结果对主机比较中,参考结果作为基准。诸如"更小""更好"等比较都是相对基准而言。
只读与可写
默认只执行 SELECT 与 SET 语句(不含 SELECT ... INTO)。若用的是可重建的测试/开发服务器、想连写语句(INSERT/UPDATE/DELETE)一起比较,指定 --no-read-only。用二进制日志时必须指定 --no-read-only,因为二进制日志里没有 SELECT。工具默认以 autocommit=1 运行,自身不创建事务(日志里的既有事务按原样执行)。
比对哪些方面
对每条查询,从两台主机的执行中比较以下方面,任一显著不同都会被报告:
- 行数(Row count):返回行数应一致,差异报为"missing rows"(归入 Row diffs)。
- 行数据(Row data):返回的数据应一致;所有差异都算显著,包括空白字符、浮点精度等。
- 警告(Warnings):应都不产生错误或警告,或产生相同的错误/警告。
- 查询时间(Query time):执行时间应同量级或更短。
- 查询错误(Query errors):仅在一台主机报 SQL 错误,报为"Query errors"(多半是该主机独有的条件所致)。
- SQL 错误(SQL errors):两台主机都报 SQL 错误,报为"SQL errors"(语法可能本身无效)。
报告
运行时会尽快打印有差异的查询(见"何时上报查询")。为防止报告过长,查询不逐条罗列,而是按指纹(fingerprint)归为类(class)。指纹是查询的抽象形式(去掉字面量、规范化空白等),例如以下三条属于同一类,指纹为 select c from t where id=?:
SELECT c FROM t WHERE id = 1
SELECT c FROM t WHERE id=5
select c from t where id = 9每个查询类最多容纳 --max-class-size 条唯一查询(默认 1000);每类每种差异最多列举 --max-examples 个示例。同类中一个示例的差异通常代表该类全部查询,所以无需列举每一个。报告中会给出该类具有某差异的查询总数。
示例报告解读
下面是一份示例报告(节选),用于说明各段含义:
#-----------------------------------------------------------------------
# Logs
#-----------------------------------------------------------------------
File: /opt/mysql/slow.log
Size: 59700
#-----------------------------------------------------------------------
# Hosts
#-----------------------------------------------------------------------
host1:
DSN: h=127.1,P=12345
hostname: dev1
MySQL: MySQL 5.1.68
host2:
DSN: h=127.1,P=12348
hostname: dev2
MySQL: MySQL 5.5.10
########################################################################
# Query class AAD020567F8398EE
########################################################################
Reporting class because it has diffs, but hasn't been reported yet.
Total queries 1
Unique queries 1
Discarded queries 0
insert into t (id, username) values(?+)
##
## Warning diffs: 1
##
-- 1.
Code: 1265
Level: Warning
Message: Data truncated for column 'username' at row 1
vs.
No warning 1265
INSERT INTO t (id, username) VALUES (NULL, 'long_username')
#-----------------------------------------------------------------------
# Stats
#-----------------------------------------------------------------------
failed_queries 0
not_select 0
queries_filtered 0
queries_no_diffs 0
queries_read 1
queries_with_diffs 1
queries_with_errors 0Query class <ID> 段最重要,列出 "QUERY DIFFERENCES"。段首先说明该类被上报的原因,接着是该类的查询计数,然后是定义该类的指纹。其后是导致上报的各类型差异:每种差异以一个双井号标题开头,列出类型与该类含此差异的查询总数,随后最多 --max-examples 个编号示例("– 1."、"— 2." 等),每个示例列出第一、第二台主机的差异(对应 "Hosts" 段),并给出首个暴露该差异的 SQL 语句。末尾的 "Stats" 段给出整体统计计数。
何时上报查询
一旦某类查询的任一 "QUERY DIFFERENCES" 或查询错误的示例数达到 --max-examples,该类立即上报;否则所有有差异的查询在工具结束时统一上报。例如某类找到两个查询时间差异时尚不上报,找到第三个时该类连同已发现的其它差异一起上报(该类后续仍会继续执行,但不再重复上报)。
输出与退出状态
"REPORT" 打印到 STDOUT;内部警告、错误与 --progress 打印到 STDERR。要分开两者,可这样运行:
pt-upgrade ... 1>report 2>err &然后 tail -f err 跟踪进度。
退出状态:正常结束且无内部警告/错误、也未发现 "QUERY DIFFERENCES" 时退出 0;否则非零,可能包含以下代码的组合:
1:内部错误或警告过多(见 STDERR);另见--[no]continue-on-error。4:发现了 "QUERY DIFFERENCES"(见 "REPORT")。8:--run-time超时,工具没读完日志或参考结果。
其它退出码表示工具崩溃或意外退出(错误应已打印到 STDERR)。可用逻辑与(&)检查特定码,例如退出状态 5 表示同时含 1 与 4(5 & 1、5 & 4 均为真)。
选项
| 选项 | 说明 |
|---|---|
--ask-pass | 连接 MySQL 时交互式询问密码 |
--[no]buffer-stdout | 默认开启 STDOUT 缓冲;使用 tee、kubectl logs 等后处理工具想看到实时进度时可关闭 |
-A, --charset | 类型:string。默认字符集(utf8 时设置 binmode/mysql_enable_utf8 并执行 SET NAMES UTF8) |
--config | 类型:Array。读取逗号分隔的配置文件列表;如指定必须放在命令行第一个选项的位置 |
--[no]continue-on-error | 默认:yes。解析出错时仍继续;但最多 100 个错误后停止(多半是工具 bug 或输入无效) |
--[no]create-upgrade-table | 默认:yes。创建 --upgrade-table 指定的库与表 |
--daemonize | fork 到后台并从 shell 脱离(仅 POSIX 系统) |
-D, --database | 类型:string。连接 MySQL 时的默认数据库 |
-F, --defaults-file | 类型:string。只从给定文件读取 mysql 选项,必须给绝对路径 |
--[no]disable-query-cache | 默认:yes。执行 SET SESSION query_cache_type = OFF 关闭查询缓存 |
--dry-run | 运行但不执行或比较查询;用于检查命令行选项、MySQL 连接以及日志/参考结果的解析 |
--filter | 类型:string。仅允许该 Perl 代码返回真值的事件(同 pt-query-digest 的同名选项) |
--help | 显示帮助并退出 |
-h, --host | 类型:string。要连接的主机名或 IP |
--ignore-warnings | 类型:Hash。比较警告时忽略这些 MySQL 警告代码 |
--log | 类型:string。daemonize 时把 STDOUT 与 STDERR 写入此文件(仅当指定 --daemonize 生效;文件不存在则创建,否则追加) |
--max-class-size | 类型:int;默认:1000。每个查询类中最多容纳的唯一查询数(见"报告"节) |
--max-examples | 类型:int;默认:3。每类 "QUERY DIFFERENCES" 最多列举的示例数;任一类差异的示例达到此值时该类即被上报 |
-s, --mysql_ssl | 类型:int。创建 SSL MySQL 连接 |
-p, --password | 类型:string。连接 --user 所用的 MySQL 密码 |
--pid | 类型:string。创建指定的 PID 文件;冲突规则与自动清理见官方文档 |
-P, --port | 类型:int。MySQL 端口号 |
--progress | 类型:array;默认:time,30。向 STDERR 打印进度;读取日志或参考结果时估算剩余时间。值为逗号分隔两部分:第一部分 percentage/time/iterations,第二部分为打印频率 |
--[no]read-only | 默认:yes。只执行 SELECT 与 SET 语句;--no-read-only 则执行所有语句(DROP/DELETE/UPDATE 等)。即便默认只读,也建议用仅有 SELECT 权限的用户以防工具 bug |
--report | 类型:Hash;默认:hosts, logs, queries, stats。打印"报告"中的这些段落 |
--run-time | 类型:time。运行多久后退出;默认一直跑到读完日志或参考结果为止 |
--save-results | 类型:string。把参考结果保存到该目录;仅当指定一个 DSN(生成参考结果)时有效。与参考结果比较时,把其结果目录代替 DSN 传入。参考结果可能占用大量磁盘空间 |
--set-vars | 类型:Array。以逗号分隔的 变量=值 列表设置 MySQL 变量;默认 wait_timeout=10000;无法设置时打印警告并继续 |
-S, --socket | 类型:string。连接使用的 socket 文件 |
--type | 类型:string;默认:slowlog。日志类型:slowlog(慢日志)、genlog(general 日志)、binlog(二进制日志,由 mysqlbinlog 转换)、tcpdump(tcpdump 文件)、rawlog(每行一条 SQL 的自定义日志) |
--upgrade-table | 类型:string;默认:percona_schema.pt_upgrade。用于清除警告的表;每次执行查询前在每台主机执行 SELECT * FROM --upgrade-table LIMIT 1 清除上次查询的警告。库表会自动创建,除非指定 --no-create-upgrade-table;不存在时建表定义为 CREATE TABLE pt_upgrade (id INT NOT NULL PRIMARY KEY) |
-u, --user | 类型:string。MySQL 用户(若非当前系统用户) |
--version | 显示版本并退出 |
--[no]version-check | 默认:yes。检查 Percona Toolkit、MySQL 等软件的最新版本与已知问题版本(详见版本检查) |
--watch-server | 类型:string。仅解析该 IP:port 的 tcpdump 事件(--type tcpdump);未指定则监视所有使用 3306 或 "mysql" 端口的服务器。非标准端口必须显式指定 IP 与端口 |
DSN 选项
| 键 | DSN 部分 | 说明 |
|---|---|---|
A | charset | 默认字符集 |
D | database | 默认数据库 |
F | mysql_read_default_file | 只从给定文件读取默认选项 |
h | host | 要连接的主机 |
L | (无前缀) | 显式启用 LOAD DATA LOCAL INFILE;部分发行版编译时未开此选项会导致异常,此选项用于重新开启 |
p | password | 连接密码(含逗号需转义) |
P | port | 连接端口 |
S | mysql_socket | 连接使用的 socket 文件 |
u | user | 登录用户(若非当前用户) |
s | mysql_ssl | 创建 SSL 连接 |
其他信息
作者:Daniel Nichter
通用说明:已知问题的反馈方式、PTDEBUG 调试安全提示、系统要求基线,见 通用说明。
更多细节请阅读 官方文档。