死锁由事务交叉加锁导致,数据库自动终止并报错;需开启日志记录、分析进程等待链、结合pg_locks与pg_stat_activity视图定位阻塞源,统一事务操作顺序以预防。

PostgreSQL 死锁通常发生在多个事务相互等待对方持有的锁,导致彼此无法继续执行。虽然死锁本身是应用层设计问题,但数据库会自动检测并终止其中一个事务,返回 deadlock detected 错误。要排查和定位这类问题,需要结合日志、系统视图和应用行为进行分析。
开启死锁日志记录
PostgreSQL 默认可能未开启详细的死锁日志,需确保以下参数在 postgresql.conf 中正确配置:
- log_lock_waits = on:记录长时间等待锁的操作
- log_statement = 'none' 或 'ddl'/'mod':根据需要记录语句
- log_min_duration_statement = -1:避免记录所有语句(可临时开启)
- log_error_verbosity = default:确保错误信息足够详细
最关键的是确保 logging_collector = on,以便将日志写入文件。重启或重载配置后,一旦发生死锁,日志中会出现类似如下内容:
ERROR: deadlock detected DETAIL: Process 12345 waits for ShareLock on transaction 67890; blocked by process 6789. Process 6789 waits for ShareLock on transaction 12345; blocked by process 12345. HINT: See server log for query details. CONTEXT: while locking tuple (1,2) in relation "orders"从日志中提取关键信息
日志中的死锁详情包含重要线索:
- 涉及的进程 PID:可用于关联具体会话
- 等待的锁类型:如行锁(tuple)、表锁、事务ID锁等
- 阻塞链:谁在等谁,形成环路
- CONTEXT 提示:指出具体表和行(如 relation "orders")
- SQL 语句内容:若开启了足够日志级别,能看到被中断的语句
通过这些信息可以还原出两个或多个事务的操作顺序,判断是否因交叉更新不同表或同一表的不同行导致资源争抢。
使用系统视图辅助诊断
在死锁发生时或复现过程中,可查询以下视图获取实时锁状态:
- pg_locks:查看当前所有锁持有和等待情况
- pg_stat_activity:查看活跃会话及其执行的 SQL
- pg_blocking_pids(pid):快速找出阻塞某进程的其他进程 ID
常用查询示例:
该查询列出所有正在等待的锁请求。结合 pg_stat_activity 可看到对应 SQL:
SELECT a.pid, a.query, a.state, l.mode FROM pg_stat_activity a JOIN pg_locks l ON a.pid = l.pid WHERE l.relation = 'orders'::regclass AND l.granted = false;分析事务执行顺序与代码逻辑
死锁的根本原因多为事务未按一致顺序访问资源。例如:
- 事务 A 先更新 orders 再更新 inventory
- 事务 B 先更新 inventory 再更新 orders
当两个事务并发执行时,就可能互相持有对方需要的行锁,形成死锁。解决方案是统一操作顺序,比如都先操作 orders 再操作 inventory。
另外注意长事务、未提交事务、连接池中连接复用导致事务延续等问题。建议:
- 尽量缩短事务范围,避免在事务中做耗时操作
- 显式控制事务边界,及时提交或回滚
- 对高频更新场景考虑使用 SELECT FOR UPDATE OF 显式加锁并保持顺序
基本上就这些。死锁虽不可避免完全杜绝,但通过合理设计事务逻辑、开启日志监控、定期审查慢锁等待,能大幅降低发生频率,并快速定位根因。










