Skip to content

pt-kill

按指定规则匹配 SHOW PROCESSLIST 中的查询,并杀掉或打印它们——俗称"慢查询狙击手"。

会杀连接/查询

本工具会真的终止 MySQL 连接或查询。它有大量特殊规则(如默认不杀复制线程),但一旦匹配条件写错,可能误杀重要连接。上线前务必先用 --print(只读打印)或 --test-matching(离线演练)确认匹配范围,并在非生产环境先验证。

安全提示

不要在命令行用 --password 提供 MySQL 密码:命令行密码对系统上所有用户可见,且会存入 ps 命令的采集输出。请使用 MySQL 选项文件或 --ask-pass

语法

bash
pt-kill [OPTIONS] [DSN]

pt-kill 连接 MySQL,从 SHOW PROCESSLIST 取查询(除非给定 --test-matching,此时从文件读 SHOW PROCESSLIST 输出;文件为 - 则从 STDIN 读)。

杀掉运行超过 60 秒的查询:

bash
pt-kill --busy-time 60 --kill

只打印、不杀掉运行超过 60 秒的查询:

bash
pt-kill --busy-time 60 --print

每 10 秒检查一次,杀掉所有 Sleep 状态的进程:

bash
pt-kill --match-command Sleep --kill --victims all --interval 10

打印所有 login 状态的进程:

bash
pt-kill --match-state login --print --victims all

看看当前 processlist 里哪些查询会命中(先把快照存文件,再离线演练):

bash
mysql -e "SHOW PROCESSLIST" > proclist.txt
pt-kill --test-matching proclist.txt --busy-time 60 --print

用法示例

以下命令假定已通过选项文件或本机 socket 配置好 MySQL 连接。pt-kill 会真正终止连接/查询,上线前务必先用 --print--test-matching 确认匹配范围,再换成 --kill

场景:只杀某个用户的长查询

某个应用账号的连接长时间不释放,只针对它杀,避免误伤其他用户:

bash
pt-kill --match-user app --busy-time 60 --kill

场景:只杀某个库的慢查询

只终止 appdb 库里超过 30 秒的查询、但保留连接(用 --kill-query):

bash
pt-kill --match-db appdb --busy-time 30 --kill-query

场景:按 SQL 内容精准匹配

某条已知的坏 SQL(如全表扫描 orders)反复拖库,按 Info 正则精确命中后杀掉:

bash
pt-kill --match-info 'SELECT.*FROM orders' --busy-time 10 --kill-query

场景:清理空闲过久的连接

清掉空闲超过 5 分钟(Command=Sleep)的连接,回收连接池泄漏的僵尸会话:

bash
pt-kill --idle-time 300 --kill --victims all

场景:后台守护自动杀慢查询

常驻后台、每隔一段时间自动杀掉超过 30 秒的查询,日志落盘:

bash
pt-kill --busy-time 30 --kill --daemonize --log /tmp/pt-kill.log

场景:防缓存风暴,保留最老一条

某条查询因缓存失效被打爆、大量相同 SQL 积压时,按指纹分组、只留最老那条完成并回填缓存,杀掉其余重复查询:

bash
pt-kill --group-by fingerprint --busy-time 5 --victims all-but-oldest --kill-query

场景:优雅停止所有运行中的实例

通过 sentinel 文件一次性停掉所有监听同一文件的 pt-kill 实例:

bash
pt-kill --stop

功能说明

pt-kill 抓取 SHOW PROCESSLIST 中的查询,经过过滤,然后杀掉或打印。通常它连接 MySQL 取查询;也可以从文件读 SHOW PROCESSLIST 输出——这种情况下不连 MySQL,--kill 无效,应改用 --print。这种"先存快照、后演练"的能力正是 --test-matching 的用处:确认你的匹配条件杀对了查询。

两个关键选项:--busy-time 匹配查询的 Time 值(SHOW PROCESSLIST 的 Time 列),而大多数 --match-* 匹配的是对应列(如 --match-command 匹配 Command 值);--victims 控制每个 class 里命中的查询杀哪些,默认杀 Time 最大的(最老的)那条。

通常至少要指定一个 --match 选项,否则没有查询会命中;或者用 --match-all 匹配所有未被 --ignore 忽略的查询。

分组、匹配与击杀

查询经过几步确定最终杀谁(或打印谁):

  1. 分组(grouping):由 --group-by 控制。默认不分组,所有查询归到一个默认 class。后续所有匹配与过滤都按 class 进行,所以可以只对某些 class 匹配、过滤。
  2. 匹配(matching):先按各 Query Matches 选项(如 --match-user)从 class 里过滤掉不匹配的查询;再按各 Class Matches 选项(如 --query-count)过滤整个 class。
  3. 选 victim:由 --victims 控制每个 class 里命中查询杀哪些——可能只想杀最老的,或全杀。
  4. 动作(action):对全部 class 里命中的查询统一执行动作,顺序为 --print--execute-command--kill/--kill-query

pt-kill 会杀掉命中任一条件的查询(逻辑 OR)。例如:

bash
--busy-time 114 --match-command 'Query|Execute'

会杀掉 busy-time > 114 或 command 为 Query/Execute 的查询。若只想杀"busy-time > 114 且 command 为 Query/Execute"的,要用 --kill-busy-commands

bash
--busy-time 114 --kill-busy-commands 'Query|Execute'

匹配规则要点

所有 --match-*/--ignore-* 的模式默认大小写敏感,可用形如 (?i-xsm:select) 的正则改为不敏感。--ignore 选项优先于 --match。匹配的 command、db、host 等对应 SHOW PROCESSLIST 的列。

默认保护

默认 --[no]ignore-self 为开启,不会杀 pt-kill 自身连接;默认 --ignore-stateLocked,不会杀正在等锁的线程。默认匹配不作用于复制线程(需 --replication-threads 才匹配/可杀复制线程)。改动这些默认值前务必想清后果。

--filter 自定义 Perl 过滤

命令行上的 filter 会被包进括号,多分支写法会因此非法(错误示范):

bash
--filter 'if () { } else { }'  # WRONG

复杂多行过滤须放进文件(如 filter.txt),再用 --filter filter.txt 读取:

bash
# filter.txt 内容
my $event_ok; if (...) { $event_ok=1; } else { $event_ok=0; } $event_ok

输出

只给 --kill 时没有任何输出;只给 --print 时,对每个本会杀掉的查询打印带时间戳的 KILL 语句:

# 2009-07-15T15:04:01 KILL 8 (Query 42 sec) SELECT * FROM huge_table

该行含时间戳、查询 Id(8)、Time(42 sec)与 Info(通常是查询 SQL)。同时给 --kill--print 则既杀又打印。--execute-command 执行的命令自行负责其输出与日志,pt-kill 执行后不再控制。

日志表

--log-dsn 可把每次杀掉的查询存进一张表(--create-log-table 会按如下结构创建)。DSN 必须带库(D)和表(t);表至少包含以下列(可自加列但 pt-kill 不用):

sql
CREATE TABLE kill_log (
  kill_id     int(10) unsigned NOT NULL AUTO_INCREMENT,
  server_id   bigint(4) NOT NULL DEFAULT '0',
  timestamp   DATETIME,
  reason      TEXT,
  kill_error  TEXT,
  Id          bigint(4) NOT NULL DEFAULT '0',
  User        varchar(16) NOT NULL DEFAULT '',
  Host        varchar(64) NOT NULL DEFAULT '',
  db          varchar(64) DEFAULT NULL,
  Command     varchar(16) NOT NULL DEFAULT '',
  Time        int(7) NOT NULL DEFAULT '0',
  State       varchar(64) DEFAULT NULL,
  Info        longtext,
  Time_ms     bigint(21) DEFAULT '0', # NOTE, TODO: currently not used
  PRIMARY KEY (kill_id)
) DEFAULT CHARSET=utf8

选项

选项说明
--any-busy-time类型:time;分组:Class Matches。class 内只要有任一查询运行超过该时长即匹配整个 class(如指定 10,则至少有一条 >10 秒才匹配)。参见 --each-busy-time
--ask-pass连接 MySQL 时交互式询问密码
--[no]buffer-stdout默认开启 STDOUT 缓冲;使用 tee、kubectl logs 等后处理工具想看到实时进度时可关闭
--busy-time类型:time;分组:Query Matches。匹配运行超过该时长的查询(查询须处于 Command=Query);匹配的是 SHOW PROCESSLISTTime
--charset类型:string。默认字符集(utf8 时设置 binmode/mysql_enable_utf8 并执行 SET NAMES UTF8
--config类型:Array。读取逗号分隔的配置文件列表;如指定必须放在命令行第一个选项的位置
--create-log-table--log-dsn 指定的表不存在则创建(结构见该选项说明)
--daemonizefork 到后台并从 shell 脱离(仅 POSIX 系统)
-D, --database类型:string。连接使用的数据库
-F, --defaults-file类型:string。只从给定文件读取 mysql 选项,必须给绝对路径
--each-busy-time类型:time;分组:Class Matches。class 内每条查询都运行超过该时长才匹配整个 class。参见 --any-busy-time--busy-time
--execute-command类型:string;分组:Actions。查询命中时执行该命令;执行后 pt-kill 不再控制,命令自行负责采集/日志/间隔等。每次命中都会执行,多实例时须行为良好。pt-kill 不向命令传任何信息
--filter类型:string。用该段 Perl 代码(或含 Perl 代码的文件)过滤事件:返回真则继续、否则终止。参数为 $event 哈希引用;若值是可读文件则读取整文件作代码(文件不要有 shebang)。命令行上的 filter 会被包进括号,复杂的多行 filter 须放文件。编译失败则报错退出;运行出错(如对未定义值做模式匹配)pt-kill 不兜底,须谨慎
--fingerprint分组:Actions。修改 --print 行为,强制打印查询指纹(fingerprint)而非原始查询
--group-by类型:string。按该 SHOW PROCESSLIST 列对查询分组后再匹配;除基本列(user、host、command、state 等)外,还可按 fingerprint(抽象 Info 列的 SQL)分组。默认不分组,匹配与动作作用于所有查询;分组后可只匹配某类相似查询(如检测缓存风暴需按 arg 分组)
--help显示帮助并退出
-h, --host类型:string;默认:localhost。要连接的主机
--idle-time类型:time;分组:Query Matches。匹配空闲/Sleep 超过该时长的查询(查询须处于 Command=Sleep);匹配 SHOW PROCESSLISTTime
--ignore-command类型:string;分组:Query Matches。忽略 Command 匹配该 Perl 正则的查询。参见 --match-command
--ignore-db类型:string;分组:Query Matches。忽略 db(数据库)匹配该 Perl 正则的查询。参见 --match-db
--ignore-host类型:string;分组:Query Matches。忽略 Host 匹配该 Perl 正则的查询。参见 --match-host
--ignore-info类型:string;分组:Query Matches。忽略 Info(查询)匹配该 Perl 正则的查询。参见 --match-info
--[no]ignore-self默认:yes;分组:Query Matches。不杀 pt-kill 自身连接
--ignore-state类型:string;分组:Query Matches;默认:Locked。忽略 State 匹配该 Perl 正则的查询;默认避免杀掉正等其他线程锁的线程。参见 --match-state
--ignore-user类型:string;分组:Query Matches。忽略 user 匹配该 Perl 正则的查询。参见 --user
--interval类型:time。检查待杀查询的频率。未给 --busy-time 时默认 30 秒;否则默认为 --busy-time 的一半;两者都给则以显式 --interval 为准。参见 --run-time
--json--print 配合,把杀掉的查询以 JSON 输出;普通文本输出不再显示。格式如 {"Command":"Query","Host":"10.0.0.1:9999","Id":1234,"Info":"SELECT SLEEP(5)","State":"User sleep","Time":10,"User":"my_user","Db":"my_db","Kill_Error":"","Digest":"58A43A7DA83F58C1","Reason":"Exceeds busy time\nQuery matches Command spec","Timestamp":"2022-01-01T00:00:00"}
--json-fields类型:string。给 --json 输出附加额外的 key:value 对,格式:--json-fields key1:value1,key2:value2。例如 --print --json --json-fields hostname:$(hostname),tag:my_tag
--kill分组:Actions。杀掉命中查询所在的连接(进程/线程);只想杀单条查询而非连接用 --kill-query。除非同时给 --print,否则不打印任何匹配/杀掉信息。参见 --wait-before-kill/--wait-after-kill
--kill-busy-commands类型:string;默认:Query;分组:Actions。逗号分隔的 command 列表,运行超过 --busy-time 秒时监视/杀掉;默认只杀 Query,需杀其它(如预编译执行的 Execute)时加进来,例如 --kill-busy-commands=Query,Execute
--kill-query分组:Actions。只杀命中查询(需 MySQL 5.0+);与 --kill 杀连接不同,它只终止查询不杀连接
--log类型:string。守护进程化时把所有输出写入该文件
--log-dsn类型:DSN。把每次杀掉的查询存进该 DSN 指定的表;DSN 必须带库(D)和表(t),表结构见上文"日志表"(亦用于 --create-log-table
--match-all分组:Query Matches。匹配所有未被忽略的查询;若没给任何 --ignore 选项则每条都命中(复制线程除外,除非也指定 --replication-threads)。用于表达"除了……都匹配"的负向匹配。不同于 --victims all:本项匹配 class 内所有查询,而 --victims all 是说命中的查询全杀
--match-command类型:string;分组:Query Matches。只匹配 Command 匹配该 Perl 正则的查询。常见值:QuerySleepBinlog DumpConnectDelayed insertExecuteFetchInit DBKillPrepareProcesslistQuitReset stmtTable Dump
--match-db类型:string;分组:Query Matches。只匹配 db(数据库)匹配该 Perl 正则的查询
--match-host类型:string;分组:Query Matches。只匹配 Host 匹配该 Perl 正则的查询(Host 常含端口如 host:port
--match-info类型:string;分组:Query Matches。只匹配 Info(查询)匹配该 Perl 正则的查询(无查询时 Info 为 NULL)
--match-state类型:string;分组:Query Matches。只匹配 State 匹配该 Perl 正则的查询。常见值:Lockedlogincopy to tmp tableCopying to tmp tableCopying to tmp table on diskCreating tmp tableexecutingReading from netSending dataSorting for orderSorting resultTable lockUpdating
--match-user类型:string;分组:Query Matches。只匹配 User 匹配该 Perl 正则的查询
--mysql_ssl类型:int。创建 SSL MySQL 连接
-p, --password类型:string。连接密码(含逗号需转义)
--pid类型:string。创建指定的 PID 文件;冲突规则与自动清理见官方文档
-P, --port类型:int。连接端口
--print分组:Actions。为命中查询打印 KILL 语句,并不真杀;只想看会杀哪些用 --print,要既杀又打印则同时给 --kill
--query-count类型:int;分组:Class Matches。class 内至少这么多条查询才匹配;指定 --group-by 时仅作用于各 class,否则作用于整张 SHOW PROCESSLIST
--query-id打印刚被杀查询的 ID,等价于 pt-query-digest 的 "ID" 输出以便交叉引用。例如 Query ID 0xE9800998ECF8427E。该 ID 是查询 fingerprint 的摘要(digest),同形不同值的查询 ID 相同
--rds标明实例在 Amazon RDS 上。默认 --kill 用 MySQL 的 kill--kill-querykill query;RDS 上这两条不可用,本选项使 --kill 改用 CALL mysql.rds_kill(thread-id)--kill-query 改用 CALL mysql.rds_kill_query(thread-id)
--replication-threads分组:Query Matches。允许匹配并杀掉复制线程;默认匹配不适用于复制线程(完全忽略),指定后才可匹配(并可能杀)源与从库上的复制线程
--run-time类型:time。运行该时长后退出;默认永远运行(或被 --sentinel 文件停止)。指定后每次检查 PROCESSLIST 之间睡眠 --interval
--sentinel类型:string;默认:/tmp/pt-kill-sentinel。该文件存在则退出;会导致所有运行中的 pt-kill 实例退出,便于优雅停 cron。参见 --stop
--set-vars类型:Array。以逗号分隔的 变量=值 列表设置 MySQL 变量;默认 wait_timeout=10000;无法设置时打印警告并继续
-S, --socket类型:string。连接使用的 socket 文件
--stop创建 --sentinel 指定的文件并退出,从而停止所有监视同一 sentinel 文件的运行实例
--[no]strip-comments默认:yes。移除 PROCESSLIST 中 Info 列的 SQL 注释
--test-matching类型:array;分组:Query Matches。用含 processlist 快照的文件测试匹配选项;因为匹配条件可能复杂,可把快照存文件后离线试匹配。会禁用 --run-time--interval--[no]ignore-self
-u, --user类型:string。登录用户(若非当前用户)
-v, --verbose向 STDOUT 打印正在做什么的信息
--version显示版本并退出
--[no]version-check默认:yes。检查 Percona Toolkit、MySQL 等软件的最新版本与已知问题版本(详见版本检查
--victims类型:string;默认:oldest。每个 class 里命中查询杀哪些:oldest=只杀最老的一条(按 Time 最大,避免杀长等而非长运行的);all=杀 class 内全部;all-but-oldest=杀除最老外的全部(即只留最老的,可用于防缓存风暴:多条相同查询造成积压时只留第一条完成并填充缓存)
--wait-after-kill类型:time。杀掉一条查询后、继续找下一条前等待;让被阻塞的查询有机会执行,避免杀掉阻塞一批查询的元凶后又立刻杀掉那批
--wait-before-kill类型:time。杀查询前等待;让 --execute-command 有机会在查询被杀前看到它并采集 MySQL/系统信息

说明:必须至少指定 --kill--kill-query--print--execute-command--stop 之一。--any-busy-time--each-busy-time 互斥;--kill--kill-query 互斥;--daemonize--test-matching 互斥。

DSN 选项

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

其他信息

  • 作者:Baron Schwartz 和 Daniel Nichter

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

更多细节请阅读 官方文档

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