温馨提示×

温馨提示×

您好,登录后才能下订单哦!

密码登录×
登录注册×
其他方式登录
点击 登录注册 即表示同意《亿速云用户服务条款》

PostgreSQL为什么频繁死锁

发布时间:2026-11-07 12:33:36 来源:亿速云 阅读:101 作者:小樊 栏目:数据库

PostgreSQL 出现频繁死锁,通常不是数据库本身有 bug,而是并发访问方式、事务设计或索引/锁粒度出了问题。下面按最常见原因 → 原理 → 解决思路来说明。


一、最常见原因(90% 的死锁都在这)

1️⃣ 多个事务以不同顺序更新同一批数据

这是死锁的第一大原因。

示例:

-- 事务 A
UPDATE account SET balance=balance-100 WHERE id=1;
UPDATE account SET balance=balance+100 WHERE id=2;

-- 事务 B
UPDATE account SET balance=balance-100 WHERE id=2;
UPDATE account SET balance=balance+100 WHERE id=1;

结果:

  • A 锁 id=1
  • B 锁 id=2
  • 互相等对方释放 → 死锁

✅ 解决:

  • 所有事务统一更新顺序
  • 或一次性更新:
UPDATE account
SET balance = CASE id
  WHEN 1 THEN balance-100
  WHEN 2 THEN balance+100
END
WHERE id IN (1,2);

2️⃣ 事务太大、持有锁太久

事务里做了:

  • 网络调用
  • 外部接口
  • sleep
  • 循环处理

导致:

  • 锁长时间不释放
  • 并发一高就撞死锁

✅ 解决:

  • 事务越短越好
  • 不在事务里做 IO
  • 批量操作拆小

3️⃣ 缺少索引,导致表锁 / 行锁升级

UPDATE orders SET status='done' WHERE user_id=123;

如果 user_id 没有索引:

  • PG 会全表扫描
  • 实际上锁了很多行甚至接近表锁

✅ 解决:

CREATE INDEX idx_orders_user_id ON orders(user_id);

4️⃣ SELECT … FOR UPDATE 使用不当

SELECT * FROM account WHERE id=1 FOR UPDATE;

如果:

  • 多事务同时 FOR UPDATE
  • 顺序不一致 → 死锁

✅ 解决:

  • 只在必要时用
  • 排序后再 FOR UPDATE
SELECT * FROM account
WHERE id IN (1,2)
ORDER BY id
FOR UPDATE;

5️⃣ 外键导致“隐藏锁”

更新子表 → 自动锁父表行
更新父表 → 自动锁子表

✅ 解决:

  • 外键必须有索引
CREATE INDEX idx_child_parent_id ON child(parent_id);

二、PostgreSQL 死锁机制说明

  • PG 不会无限等待
  • 默认检测到死锁会:
    • 中断其中一个事务
    • 抛错:deadlock detected

这不是 bug,是保护机制


三、如何快速定位死锁原因

1️⃣ 看日志

log_lock_waits = on
deadlock_timeout = 1s

死锁日志会显示:

  • 哪个事务
  • 持有什么锁
  • 等什么锁

2️⃣ 实时监控锁

SELECT * FROM pg_locks;
SELECT * FROM pg_stat_activity;

3️⃣ 用扩展

CREATE EXTENSION pg_stat_statements;

看哪些 SQL 最容易导致锁冲突


四、总结一句话

PostgreSQL 频繁死锁,几乎一定是:并发事务顺序不一致 + 锁持有太久 + 缺索引

如果你愿意,可以:

  • 贴一段具体 SQL / 表结构
  • 或说明死锁日志内容

我可以直接帮你指出是哪一行在“制造死锁”。

向AI问一下细节

免责声明:本站发布的内容(图片、视频和文字)以原创、转载和分享为主,文章观点不代表本网站立场,如果涉及侵权请联系站长邮箱:is@yisu.com进行举报,并提供相关证据,一经查实,将立刻删除涉嫌侵权内容。

AI
助
手