pt-deadlock-logger 是 Percona Toolkit 中的一个命令行工具。它的核心作用就是帮你自动、持续地记录下 MySQL 数据库发生的每一次死锁。
💡 为什么需要它?
MySQL 自己提供的 SHOW ENGINE INNODB STATUS 命令虽然能看到死锁信息,但它有个很大的局限:只能看到最近一次发生的死锁。如果死锁发生得很频繁,之前的记录很快就会被覆盖掉,这就给排查问题带来了困难。
pt-deadlock-logger 正是为了解决这个问题而生的。它会定时去检查 SHOW ENGINE INNODB STATUS 的输出,一旦发现新的死锁,就把它记录下来,让你能保留一份完整的"死锁历史记录",方便事后分析。
🛠️ 它是怎么工作的?
可以把它理解成一个自动化的"死锁记录员":
- 持续监控 :工具会按设定的时间间隔(比如每30秒)去查询一次
SHOW ENGINE INNODB STATUS。 - 智能识别:它能识别出哪些死锁是"新的",从而避免重复记录同一条死锁信息。
- 保存结果 :它有两种记录方式:
- 直接输出到屏幕(
STDOUT)。 - 保存到数据库表 (通过
--dest参数),这是最推荐的方式,方便后续用SQL查询和分析。同时也可以用--log参数写入文件。
- 直接输出到屏幕(
📋 如何用它来记录死锁?
使用 pt-deadlock-logger 其实不复杂,核心步骤就两步。
第一步:准备一张存放死锁记录的表
为了将死锁信息持久化保存,需要在某个数据库中创建一张专用的表。可以手动执行建表语句,或者直接用工具自带的 --create-dest-table 参数自动创建。
推荐使用的表结构通常包含以下字段,记录了死锁发生时的详细信息:
| 字段名 | 作用 |
|---|---|
server |
发生死锁的服务器 |
ts |
检测到死锁的时间 |
thread |
发生死锁的线程ID |
txn_id |
事务ID |
user / hostname |
执行该操作的数据库用户名和主机 |
db / tbl / idx |
发生死锁的数据库、表和索引 |
lock_type / lock_mode |
锁的类型和模式 |
query |
导致死锁的SQL语句 |
victim |
该事务是否被选为"牺牲者"被回滚 |
第二步:启动监控工具
准备好存储表后,就可以运行工具来开始监控了。以下是一个基本用法的示例:
bash
pt-deadlock-logger \
--host=源数据库IP \
--user=用户名 \
--password=密码 \
--dest h=目标数据库IP,D=目标数据库,t=deadlocks,u=用户名,p=密码 \
--interval=30 \
--run-time=3600
--host和--user等是连接被监控的数据库的信息。--dest指定了存放死锁记录的数据库、表以及连接信息。--interval=30表示每30秒检查一次死锁。--run-time=3600表示运行1小时后退出。如果不加这个参数,工具会一直运行下去,通常在生产环境会配合--daemonize让它作为后台进程常驻。
总而言之,pt-deadlock-logger 就像一个自动记录死锁事件的"黑匣子",对于分析死锁频发的原因、优化应用代码非常有帮助。
模拟与验证死锁记录
为了验证效果,可以人为制造一次死锁。例如,在两个会话中,按相反的顺序更新同一张表的记录。
会话1
sql
ini
START TRANSACTION;
UPDATE t1 SET c2 = 'greatsql' WHERE id = 1;
会话2
sql
ini
START TRANSACTION;
UPDATE t1 SET c2 = 'GreatSQL' WHERE id = 2;
会话1(此时被阻塞等待)
sql
ini
UPDATE t1 SET c2 = 'greatsql' WHERE id = 2;
会话2(形成死锁,并回滚)
sql
vbnet
UPDATE t1 SET c2 = 'GreatSQL' WHERE id = 1;
-- 此时会报错:ERROR 1213 (40001): Deadlock found when trying to get lock; try restarting transaction
4. 查看记录的死锁详情
工具运行后,再去查询之前创建的 deadlocks 表,就能看到死锁的详细记录了。
| server | ts | thread | user | db | tbl | idx | lock_type | lock_mode | wait_hold | victim | query |
|---|---|---|---|---|---|---|---|---|---|---|---|
| localhost | 2024-03-20 15:12:51 | 1216 | root | test_db | t1 | PRIMARY | RECORD | X | w | 1 | UPDATE t1 SET c2 = 'GreatSQL' WHERE id = 1 |
| localhost | 2024-03-20 15:12:51 | 1230 | root | test_db | t1 | PRIMARY | RECORD | X | w | 0 | UPDATE t1 SET c2 = 'greatsql' WHERE id = 2 |
从表中可以清楚地看到:发生死锁的时间(ts)、哪个线程(thread)、在哪个库哪个表(db, tbl)、锁的类型(lock_mode)、哪个事务是牺牲者被回滚了(victim),以及导致死锁的具体SQL(query)。这个结构化信息比直接看 SHOW ENGINE INNODB STATUS 的文本要清晰得多。