pt-find
像 GNU find 一样按条件查找 MySQL 表,并对找到的表执行指定动作。
语法
pt-find [OPTIONS] [DATABASES]默认动作是打印库名和表名。
找出一天前创建、使用 MyISAM 引擎的所有表并打印表名:
pt-find --ctime +1 --engine MyISAM找出 MyISAM 表并把它们转成 InnoDB:
pt-find --engine MyISAM --exec "ALTER TABLE %D.%N ENGINE=InnoDB"找出由已不存在的进程按 name_sid_pid 命名约定创建的表,然后删掉它们:
pt-find --connection-id '\D_\d+_(\d+)$' --server-id '\D_(\d+)_\d+$' --exec-plus "DROP TABLE %s"找出 test 和 junk 两个库里的空表并删除:
pt-find --empty junk test --exec-plus "DROP TABLE %s"找出总大小超过 5GB 的表:
pt-find --tablesize +5G列出所有表的数据与索引总大小,并按最大的表排在前面(sort 是另一个程序):
pt-find --printf "%T\t%D.%N\n" | sort -rn同上,但这次把数据插回数据库留档:
pt-find --noquote --exec "INSERT INTO sysdata.tblsize(db, tbl, size) VALUES('%D', '%N', %T)"数据风险
--exec 和 --exec-plus 会真的把 SQL 执行掉,上面 DROP TABLE %s 这类用法会直接删表。 所有数据库工具都可能对系统和数据库服务器造成风险,使用前请:阅读工具文档、 查看已知问题、先在非生产服务器上测试、 备份生产服务器并验证备份可用。带破坏性动作之前,建议先用默认的打印动作确认命中的表是否符合预期。
用法示例
以下命令假定已通过 /etc/my.cnf、~/.my.cnf 或本机 socket 配置好 MySQL 连接;需显式指定连接时加 -h主机 -P端口 -u用户 -p 或 --defaults-file=/path/my.cnf。命令可直接复制,替换其中的库表名、阈值即可。
场景:排查碎片严重的表
Data_free 很大说明表有大量空洞,是 OPTIMIZE TABLE 或在线改表的候选对象;先 --print 只打印、确认范围后再动手:
pt-find --datafree +1G --print场景:找半年没改过的冷表
做归档/下线评估时,列出修改时间超过 180 天的表,连同最后修改时间和总大小一起打印:
pt-find --mtime +180 --printf "%D.%N\t%U\t%T\n"场景:巡检某库是否还有 MyISAM 表
迁移引擎前,只看 appdb 这个库里是否还残留 MyISAM 表(全局 --engine 会扫所有库,这里用 --database 收窄):
pt-find --database appdb --engine MyISAM --print场景:找行数极少的表
配置表、字典表通常行数很少;反过来拿 --rows -100 快速筛出行数小于 100 的表,定位可疑的小表:
pt-find --rows -100 --print场景:按命名约定清理临时表
按表名正则 ^tmp_ 找出临时表,务必先用 --print 确认命中名单,确认无误再把 --print 换成 --exec-plus "DROP TABLE %s" 执行删除(删表不可逆,见上方 danger 提示):
pt-find --tblregex '^tmp_' --print场景:用 OR 圈出超大或超旧的表
想一次性列出"体积超过 10G 或 一年以上没改过"的表(两个条件满足其一即可),用 --or 切换成 OR 语义:
pt-find --or --tablesize +10G --mtime +365 --print功能说明
pt-find 寻找通过你指定的测试的 MySQL 表,然后执行你指定的动作。 默认动作是把库名和表名打印到 STDOUT。
它比 GNU find 简单,不允许在命令行上写复杂的表达式。 能用 SHOW TABLES 就用 SHOW TABLES,需要时才用 SHOW TABLE STATUS。
三类选项
选项分三种:决定行为或设置的普通选项;决定某张表是否进入查找结果的测试; 以及对找到的表做点什么的动作。
pt-find 使用标准的 Getopt::Long 解析选项,所以长选项名前面要写双横线,这一点与 GNU find 不同。 多个测试之间默认是 AND 关系,用 --or 可以改成 OR;由于选项解析不是 pt-find 自己实现的, 无法写出带括号、AND 与 OR 混用的复杂表达式。
测试的取值写法
多数测试是拿 SHOW TABLE STATUS 输出的某一列去比较。数值参数可以写成 +n 表示大于 n、 -n 表示小于 n、n 表示正好等于 n;所有数值选项都可带可选的后缀倍数 k、M、G (分别是 1024、1048576、1073741824)。除注明为 SQL LIKE 模式的以外,所有模式都是 Perl 正则表达式(见 man perlre),可用 --case-insensitive 让所有正则匹配不区分大小写。
日期和时间都相对同一个瞬间来衡量,即 pt-find 第一次向数据库服务器询问当前时间的那一刻。 所有日期时间运算都在 SQL 里完成,所以"找 5 天前修改过的表"会翻译成 SELECT DATE_SUB(CURRENT_TIMESTAMP, INTERVAL 5 DAY);指定 --day-start 时基准换成 CURRENT_DATE。
不过表大小之类的指标并不是某个瞬间的一致快照:MySQL 处理完所有 SHOW 查询需要时间, pt-find 对此无能为力,这些数值就是取到它们的那一刻的值。
动作的执行顺序
--exec-plus 这个动作在其他一切之后发生,除此之外各动作的执行顺序是不确定的。
选项
| 选项 | 说明 |
|---|---|
--ask-pass | 连接 MySQL 时交互式询问密码 |
--[no]buffer-stdout | 默认开启 STDOUT 缓冲;使用 tee、kubectl logs 等后处理工具想看到实时进度时可关闭 |
--case-insensitive | 让所有正则表达式搜索不区分大小写 |
-A, --charset | 类型:string。默认字符集(utf8 时设置 binmode/mysql_enable_utf8 并执行 SET NAMES UTF8) |
--config | 类型:Array。读取逗号分隔的配置文件列表;如指定必须放在命令行第一个选项的位置 |
-D, --database | 类型:string。连接到这个数据库 |
--day-start | 时间(用于 --mmin 等)从今天零点起算,而不是从当前时刻起算 |
-F, --defaults-file | 类型:string。只从给定文件读取 mysql 选项,必须给绝对路径 |
--help | 显示帮助并退出 |
-h, --host | 类型:string。要连接的主机 |
-s, --mysql_ssl | 类型:int。创建 SSL MySQL 连接 |
--or | 各测试之间用 OR 而不是 AND 组合。默认所有测试之间视为 AND,该选项切换成 OR。由于选项解析不是 pt-find 自己实现的,无法指定带括号以及 OR 与 AND 混用的复杂表达式 |
-p, --password | 类型:string。连接密码(含逗号需用反斜杠转义,如 exam\,ple) |
--pid | 类型:string。创建指定的 PID 文件;若文件已存在且其中的 PID 与当前 PID 不同则不启动,但若其中的 PID 已不在运行则覆盖该文件。工具退出时自动删除 |
-P, --port | 类型:int。连接端口 |
--[no]quote | 默认:yes。用 MySQL 标准的反引号包裹 MySQL 标识符名。加引号发生在测试运行之后、动作运行之前(所以正则匹配的是不带反引号的名字,而 --exec 拿到的是带反引号的名字);把库表名当成字符串值拼进 SQL 时需要用 --noquote 关掉 |
--set-vars | 类型:Array。以逗号分隔的 变量=值 列表设置 MySQL 变量;默认 wait_timeout=10000,命令行指定的值覆盖默认值;无法设置时打印警告并继续 |
-S, --socket | 类型:string。连接使用的 socket 文件 |
-u, --user | 类型:string。登录用户(若非当前用户) |
--version | 显示版本并退出 |
--[no]version-check | 默认:yes。检查 Percona Toolkit、MySQL 等软件的最新版本与已知问题版本(详见版本检查) |
本工具还接受额外的命令行参数(要搜索的数据库列表),详见"语法"一节。
测试
数值参数的 +n / -n / n 写法与 k/M/G 后缀见"测试的取值写法"。 如果你需要的测试不在下表中,可以到 https://jira.percona.com/projects/PT 提功能需求。
| 测试 | 说明 |
|---|---|
--autoinc | 类型:string。表的下一个 AUTO_INCREMENT 值为 n,检查 Auto_increment 列 |
--avgrowlen | 类型:size。表的平均行长为 n 字节,检查 Avg_row_length 列。可写 NULL 以匹配 Avg_row_length IS NULL |
--checksum | 类型:string。表校验和(checksum)为 n,检查 Checksum 列 |
--cmin | 类型:size。表创建于 n 分钟前,检查 Create_time 列 |
--collation | 类型:string。表的排序规则匹配该模式,检查 Collation 列 |
--column-name | 类型:string。表中有列名匹配该模式 |
--column-type | 类型:string。表中有列的类型匹配该类型(不区分大小写)。类型举例:varchar、char、int、smallint、bigint、decimal、year、timestamp、text、enum |
--comment | 类型:string。表注释匹配该模式,检查 Comment 列 |
--connection-id | 类型:string。表名中带有已不存在的 MySQL 连接 ID。参数必须是能捕获数字的 Perl 正则,如 (\d+);表名匹配该模式时,捕获到的数字被当作某个进程的连接 ID,若按 SHOW FULL PROCESSLIST 该连接不存在,测试返回真;若该连接 ID 大于 pt-find 自己的连接 ID,为安全起见返回假。用途:使用 MySQL 基于语句的复制时,临时表很麻烦,有人改用带唯一名字的真实表,比如把连接 ID 拼到表名末尾(scratch_table_12345),既保证唯一又能追溯归属,更重要的是连接一旦不存在,就可以认为它是没清理临时表就死掉了,这张表可以删。推荐的参数是 '\D_(\d+)$'——找末尾是一串数字、前面是下划线加一个非数字字符的表(后一个条件避免误伤 baron_scratch_2007_05_07 这种以日期结尾的表)。参见 --server-id |
--createopts | 类型:string。表的创建选项匹配该模式,检查 Create_options 列 |
--ctime | 类型:size。表创建于 n 天前,检查 Create_time 列 |
--datafree | 类型:size。表有 n 字节空闲空间,检查 Data_free 列。可写 NULL 以匹配 Data_free IS NULL |
--datasize | 类型:size。表数据占用 n 字节,检查 Data_length 列。可写 NULL 以匹配 Data_length IS NULL;注意从 MySQL 8.0 起空表返回 0 而不是 NULL |
--dblike | 类型:string。库名匹配该 SQL LIKE 模式 |
--dbregex | 类型:string。库名匹配该模式 |
--empty | 表没有行,检查 Rows 列 |
--engine | 类型:string。表的存储引擎匹配该模式,检查 Engine 列(更早的 MySQL 版本里是 Type 列) |
--function | 类型:string。函数定义匹配该模式 |
--indexsize | 类型:size。表索引占用 n 字节,检查 Index_length 列。可写 NULL 以匹配 Index_length IS NULL |
--kmin | 类型:size。表在 n 分钟前被检查过,检查 Check_time 列 |
--ktime | 类型:size。表在 n 天前被检查过,检查 Check_time 列 |
--mmin | 类型:size。表最后修改于 n 分钟前,检查 Update_time 列 |
--mtime | 类型:size。表最后修改于 n 天前,检查 Update_time 列 |
--procedure | 类型:string。存储过程定义匹配该模式 |
--rowformat | 类型:string。表的行格式匹配该模式,检查 Row_format 列 |
--rows | 类型:size。表有 n 行,检查 Rows 列。可写 NULL 以匹配 Rows IS NULL |
--server-id | 类型:string。表名中含有 server ID。如果你按 --connection-id 说明的命名约定创建临时表,同时还把创建这些表的服务器的 server ID 也写进表名,就可以用这个模式匹配确保表只在创建它的那台服务器上被删除,避免副本上还在用的表被误删(前提是各服务器的 server ID 唯一,而这本来就是复制正常工作的要求)。例如在复制源(server ID 22)上创建了 scratch_table_22_12345,在副本(server ID 23)上看到这张表时,你可能以为没有 12345 号连接就能安全删除;但只要用 --server-id '\D_(\d+)_\d+$' 强制表名必须匹配本机 server ID,这张表在副本上就不会被删 |
--tablesize | 类型:size。表占用 n 字节,检查 Data_length 与 Index_length 两列之和 |
--tbllike | 类型:string。表名匹配该 SQL LIKE 模式 |
--tblregex | 类型:string。表名匹配该模式 |
--tblversion | 类型:size。表版本为 n,检查 Version 列 |
--trigger | 类型:string。触发器的动作语句匹配该模式 |
--trigger-table | 类型:string。--trigger 定义在匹配该模式的表上 |
--view | 类型:string。CREATE VIEW 匹配该模式 |
用 --connection-id 判断"表可以删"需要 PROCESS 权限
如果要这么用,请确保 pt-find 运行所用的账号具有 PROCESS 权限, 否则它只能看到同一用户的连接,可能把仍在使用中的表误判为可以删除。为安全起见,pt-find 会替你检查这一点。
动作
| 动作 | 说明 |
|---|---|
--exec | 类型:string。对找到的每一项执行该 SQL。SQL 中可以使用转义序列和格式化指令(见 --printf) |
--exec-dsn | 类型:string。以 键=值 形式指定执行 --exec 和 --exec-plus 的 SQL 时使用的 DSN。未指定的值继承命令行参数 |
--exec-plus | 类型:string。把找到的所有项一次性交给该 SQL 执行,与 --exec 不同:没有转义和格式化指令,只有一个特殊占位符 %s 代表库表名列表——找到的表会用逗号连成一串,替换到 %s 所在的位置。例如用它删掉找到的所有表:DROP TABLE %s。相当于 GNU find 的 -exec command {} + 写法 |
--print | 打印库名和表名并换行。没有指定其他动作时这是默认动作 |
--printf | 类型:string。按格式打印到标准输出,解释 \ 转义与 % 指令。转义是反斜杠开头的字符,如 \n、\t,由 Perl 解释,因此 Perl 认识的转义都能用。指令一律按 %s 替换,目前不能附加字段宽度、对齐之类的格式说明。指令列表见下表;% 后面跟一个不在表中的字符时该指令被丢弃(但那个字符照样打印) |
--printf 指令
多数指令直接来自 SHOW TABLE STATUS 的列。如果该列为 NULL 或不存在,输出中得到空字符串。
| 指令 | 数据来源 | 说明 |
|---|---|---|
%a | Auto_increment | |
%A | Avg_row_length | |
%c | Checksum | |
%C | Create_time | |
%D | Database | 表所在的库名 |
%d | Data_length | |
%E | Engine | 更早的 MySQL 版本里是 Type |
%F | Data_free | |
%f | Innodb_free | 从 Comment 字段解析而来 |
%I | Index_length | |
%K | Check_time | |
%L | Collation | |
%M | Max_data_length | |
%N | Name | |
%O | Comment | |
%P | Create_options | |
%R | Row_format | |
%S | Rows | |
%T | Table_length | Data_length 与 Index_length 之和 |
%U | Update_time | |
%V | Version |
DSN 选项
DSN 由 键=值 组成,逗号分隔;键区分大小写(P 和 p 不是同一个键); = 前后不能有空白,值含空白必须加引号。
| 键 | 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 连接 |
其他信息
作者:Baron Schwartz
通用说明:已知问题的反馈方式、PTDEBUG 调试安全提示、系统要求基线,见 通用说明。
更多细节请阅读 官方文档。