“以一致的顺序获取锁“是预防死锁最核心、最有效的手段

"以一致的顺序获取锁"是预防死锁最核心、最有效的手段。要彻底理解它,我们可以从死锁产生的底层原理出发,再结合具体的代码实践来看。

1. 底层原理:破坏"循环等待"条件

从操作系统和数据库的并发控制原理来看,死锁的产生必须同时满足四个必要条件:互斥、占有和等待、不剥夺、循环等待。

"以一致的顺序获取锁"的本质,就是从物理上破坏"循环等待"条件。

假设有两个并发事务需要同时操作资源 A 和 资源 B:

  • 不一致的顺序(产生死锁) :
    • 事务 1:先锁 A,再锁 B
    • 事务 2:先锁 B,再锁 A
    • 结果:事务 1 持有 A 等待 B,事务 2 持有 B 等待 A,形成闭环,互相卡死。
  • 一致的顺序(避免死锁) :
    • 事务 1:先锁 A,再锁 B
    • 事务 2:也先锁 A,再锁 B
    • 结果:如果事务 1 先抢到了 A,事务 2 会在尝试锁 A 时直接被阻塞排队。等事务 1 执行完释放了 A 和 B,事务 2 才能继续。交叉加锁的路径被消除,死锁自然无法发生。

2. 在 PostgreSQL 中的具体实践

在实际的数据库表设计和业务代码中,"一致的顺序"通常体现在以下两个维度:

维度一:跨表操作时,固定表的访问顺序

如果你的业务逻辑需要同时更新多张表(例如订单表和库存表),必须在全局约定一个固定的访问路径(例如:永远先锁 orders 表,再锁 products 表)。

维度二:批量操作同表多行时,强制按主键排序

这是后端开发中最容易踩坑的场景。比如批量更新一批商品库存,如果直接按前端传来的 ID 顺序去加锁,极易引发死锁。

在 Python 后端中,正确的做法是在应用层先对 ID 列表进行排序,或者在 SQL 中强制指定顺序:

python 复制代码
# 假设 items 是从前端或上游拿到的待更新商品列表
# 错误做法:直接按原顺序加锁
# for item in items:
#     update_stock(item.id)

# 正确做法:在 Python 应用层按 ID 升序排序,确保所有并发请求拿锁顺序一致
items.sort(key=lambda x: x['id']) 

for item in items:
    # 在事务中执行更新
    cursor.execute("UPDATE products SET stock = stock - 1 WHERE id = %s", (item['id'],))

或者直接在 SQL 层面利用 SELECT ... FOR UPDATE 配合 ORDER BY 来预锁定资源:

sql 复制代码
BEGIN;
-- 核心:通过 ORDER BY id 强制按主键升序获取行级锁
SELECT * FROM products WHERE id IN (1, 5, 3) ORDER BY id ASC FOR UPDATE;
-- 此时 ID 为 1, 3, 5 的行已按顺序被锁定,后续执行更新绝对安全
UPDATE products SET stock = stock - 1 WHERE id IN (1, 5, 3);
COMMIT;

3. 进阶注意事项

  • 排序字段必须命中索引 :如果 ORDER BY 的字段没有索引,数据库可能会触发全表扫描或 filesort,导致锁范围扩大甚至锁升级,反而增加死锁风险。
  • 警惕隐式加锁 :除了显式的 UPDATE 或 SELECT FOR UPDATE,外键约束、UPSERT(INSERT ... ON CONFLICT)等操作在底层也是逐行加锁的。如果并发事务处理的数据集有交集且传入顺序不同,同样会死锁。因此,在 Python 中写入数据前,养成先对数据集按唯一约束字段排序的习惯是非常必要的。
  • 应用层兜底 :即使设计了完美的加锁顺序,极端并发下仍可能有意外。建议在 Python 代码中捕获 PostgreSQL 的死锁错误码(40P01),并加入指数退避的重试机制。

相关推荐
傅科摆 _ py20 小时前
DP,贪心,堆和栈,二分力扣复习
开发语言·python·算法
吴名氏.20 小时前
Java转AI Agent教程(第一周)让模型先跑起来
人工智能·python·ai·ai agent
第404号工位21 小时前
离职退电脑前别只点“清空回收站”!手把手教你彻底抹除凭证与数字痕迹
linux·python·架构
Metaphor69221 小时前
使用 Python 在 Word 文档中添加、修改和删除变量
python·word
終不似少年遊*1 天前
【AI Coding实战方法论】从计划协作到架构护栏,一套能长期复用的工程流程
人工智能·python·团队开发·ai编程·ai coding·skills·上下文管理
lusklusklusk1 天前
Python 全局变量与局部变量区别——global 关键字、__main__ 块与作用域示例
开发语言·前端·python
马剑威(威哥爱编程)1 天前
Python 智能体开发实战 01:装了三遍环境还在报错?虚拟环境和 DeepSeek 一次配到位
开发语言·人工智能·python·agent
卷无止境1 天前
Skill 的安全边界在哪里:风险来源、验证思路与用户可用的防线
后端·python
TomEval1 天前
【测AI】第22篇:Agent 系统测试方法 — AI 学会“动手“了
人工智能·python·prompt·aigc·测试用例
速易达网络1 天前
如何“训练”一个 .task 模型
python