PostgreSQL死锁日志需同时满足log_min_messages≥ERROR且log_lock_waits=on,核心是连续三段式输出:ERROR行+两个DETAIL块,分别描述victim事务和阻塞事务的进程号、SQL、等待锁、持有锁及物理上下文。

怎么看 PostgreSQL 的 deadlock 日志条目
PostgreSQL 默认在 log_min_messages ≥ ERROR 且 log_lock_waits 开启时,才会记录死锁事件。真正关键的是日志里连续出现的三段式输出:一个 ERROR: deadlock detected 错误行,紧接着是两个(或多个)DETAIL: 块,分别描述被选为 victim 的事务和另一个/多个阻塞它的事务。
重点看每段 DETAIL: 里的这几项:
-
Process—— 进程号 + 当前正在执行的 SQLXXXX: UPDATE t1 SET x = 1 WHERE id = 100 -
Waiting for ShareLock on transaction—— 它卡在等哪个事务释放锁YYYYY -
Process—— 另一个进程的 SQL,它持有了前者需要的锁ZZZZ: SELECT * FROM t2 WHERE id = 200 FOR UPDATE -
Context: while updating tuple (0,1) in relation "t1"—— 具体到页和行的物理位置(对排查热点行很有用)
如何定位死锁涉及的表、索引与事务顺序
从日志中提取出所有出现的 UPDATE/DELETE/SELECT ... FOR UPDATE 语句,逐条检查它们是否:
- 操作了相同表但顺序相反:比如事务 A 先更新
t1再更新t2,事务 B 却先t2后t1—— 这是最常见根源 - 用了不同索引路径:同一条
UPDATE在不同事务中因WHERE条件走不同索引,导致加锁顺序不一致 - 隐式锁升级:例如
UPDATE本该只锁匹配行,但因缺失索引导致全表扫描,实际锁了整页甚至整个二级索引 - 触发器或函数调用引入了额外 DML:日志里没显示,但实际执行流中悄悄改了另一张表
用 \d+ 表名 查索引定义,用 EXPLAIN (ANALYZE, BUFFERS) 对比各条 SQL 的执行计划,确认锁范围是否预期一致。
MySQL 的 InnoDB deadlock log 怎么读(show engine innodb status 输出)
MySQL 不写 error log,而是把最近一次死锁详情塞进 SHOW ENGINE INNODB STATUS 的 LATEST DETECTED DEADLOCK 区块。注意这个输出只保留最后一次,要抓实时日志得靠定期轮询或开启 innodb_print_all_deadlocks = ON(写入 error log)。
关键字段包括:
-
*** (1) TRANSACTION:和*** (2) TRANSACTION:—— 两个冲突事务的 ID、隔离级别、等待时间 -
*** (1) HOLDS THE LOCK(S):—— 它已经持有的 record lock / gap lock 范围(如0x7f8a1c012a80是锁结构地址,重点看后面index `idx_a` of table `db`.`t`) -
*** (1) WAITING FOR THIS LOCK TO BE GRANTED:—— 它正等待的锁类型和索引键值(如lock_mode X locks rec but not gap waiting+0: len 4; hex 80000064; asc d;;) - 最后的
WE ROLL BACK TRANSACTION (1)—— 明确谁被干掉了
十六进制键值(如 80000064)需转成十进制理解:这是有符号 int 的补码表示,80000064 → -2147483548?不对,实际是 0x64 = 100,前面 800000 是符号位填充,直接取低 4 字节后转整数即可(可用 Python int('64', 16) 快速验证)。
为什么开了 log_lock_waits 还看不到死锁日志
不是所有“锁等待”都会升级成死锁,更不是所有死锁都一定被记录——常见遗漏点:
-
log_lock_waits = on只记录**超时等待**(由lock_timeout触发),而死锁检测是独立机制,必须同时设log_min_messages = 'error'或更低(如warning)才能捕获deadlock detected - 日志输出目标被重定向:检查
logging_collector = on,以及log_destination是否包含stderr或csvlog,避免只写到控制台却没落盘 - 事务在死锁前已断开:客户端崩溃或网络中断导致 backend 进程提前退出,PostgreSQL 可能来不及写完整日志
- 使用了逻辑复制或 FDW:跨节点事务的锁不参与本地死锁检测,这类“分布式死锁”不会触发本地日志
最稳妥的方式是配合 pg_stat_activity 实时查 state = 'active' 且 wait_event_type = 'Lock' 的会话,并结合 pg_locks 关联锁对象,而不是只依赖日志回溯。










