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

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

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),并加入指数退避的重试机制。

相关推荐
IvorySQL7 小时前
PostgreSQL 日报|修复截断 zstd 备份检测(10 月 9 日)
数据库·postgresql
全栈练习生9 小时前
AI Agent 沙箱
python·ai
SEO_juper10 小时前
用 Python 写一个 GEO 可见性检查脚本:你的网站现在能被 AI 引用吗
开发语言·人工智能·爬虫·python·seo·外贸独立站
用户83562907805111 小时前
使用 Python 对 Excel 工作表进行排序
后端·python
for_ever_love__12 小时前
MySQL 全文索引实战:FULLTEXT、ngram 中文分词与 MATCH AGAINST 到底该怎么用
java·python·mysql·全文检索·分词·索引·ngram
用户83562907805112 小时前
使用 Python 合并 PowerPoint 演示文稿
后端·python
weixin_4617694012 小时前
VS Code 中创建 Jupyter 文件(.ipynb)
ide·python·jupyter
2601_9669496512 小时前
多市场量化策略的数据接口应该如何设计:从数据层架构到策略接入
开发语言·python·数据分析·pandas·量化交易·股票数据·quantdash
全栈弄潮儿12 小时前
Python实战第1期:Python环境搭建与第一个程序
python
心易行者12 小时前
Agent应用+API端点商业化进阶实战:从单体智能体到可付费调用的API全流程
运维·服务器·人工智能·python·apache