SELECT FOR UPDATE本身不防死锁,但能快速暴露死锁:它使事务在无法获取锁时立即报错,避免环形等待;真正预防需统一加锁顺序、缩小锁范围并确保索引命中。

SELECT FOR UPDATE 本身不导致死锁,死锁的根源是多个事务对同一组行加锁的顺序不一致。 它只是暴露了逻辑缺陷的“照妖镜”——只要加锁路径存在环路,InnoDB 就会检测并回滚其中一个事务,报错 Deadlock found when trying to get lock(错误码 1213)。
为什么多事务按不同顺序 SELECT FOR UPDATE 会死锁
InnoDB 在加锁过程中会按索引顺序逐行获取 X 锁。当两个事务以相反顺序访问相同主键时,极易形成循环等待:
- 事务 A 执行
SELECT * FROM orders WHERE id IN (101, 205) FOR UPDATE,优化器按索引顺序锁住101 → 205 - 事务 B 同时执行
SELECT * FROM orders WHERE id IN (205, 101) FOR UPDATE,却因查询计划差异锁住205 → 101 - 结果:A 持有 101 的锁、等待 205;B 持有 205 的锁、等待 101 → 死锁触发
这种现象在批量更新、分页任务队列(如 SELECT ... FOR UPDATE LIMIT 10)中高频出现。不加 ORDER BY id 会导致结果集顺序不可控,锁序天然不一致。
为什么查不到记录也会死锁
在默认的 REPEATABLE READ 隔离级别下,SELECT ... FOR UPDATE 即使没命中任何行,也会加间隙锁(Gap Lock)。多个事务同时执行:
-
SELECT * FROM users WHERE phone = '13800138000' FOR UPDATE(该手机号不存在) - 各自在
(prev_phone, next_phone)间隙上成功加 Gap Lock(间隙锁之间兼容) - 接着都尝试
INSERT INTO users (phone, ...) VALUES ('13800138000', ...) - 插入前需申请插入意向锁(Insert Intention Lock),但该锁与 Gap Lock 互斥 → 双方互相等待
这就是典型的“查无结果 → 加间隙锁 → 插入意向锁冲突”死锁链。切到 READ COMMITTED 可禁用间隙锁,但会丢失可重复读语义,业务需自行兜底幻读。
安装、修复、更新或打包一个基于 Python 和 PyMySQL 实现的 Windows `mysql` 命令。适用于用户希望在 PowerShell/CMD 中输入 `mysql` 时重定向到 Python 实现、需要交互式输入 MySQL 连接信息、需要同步到当前用户本地 bin 目录,或需要将该能力封装成可公开复用的 Codex skill。
为什么没走索引会让死锁概率飙升
WHERE 条件未命中索引时,SELECT ... FOR UPDATE 会退化为全表扫描,并对所有扫描行加 X 锁。在 RR 级别下,这等价于锁住整个聚簇索引 —— 也就是事实上的表锁。
- 常见失效场景:
status = ''(字段无索引)、name LIKE '%abc'、user_id = '123'(隐式类型转换)、DATE(created_at) = '2026-11-04' - 用
EXPLAIN FORMAT=JSON查看key字段是否为null,或key_locks显示范围过大,就是危险信号 - 一旦锁表,任意其他事务对该表的
UPDATE、SELECT FOR UPDATE、甚至ALTER TABLE(因 MDL 冲突)都会被阻塞,死锁面指数级扩大
为什么 LOCK IN SHARE MODE + UPDATE 更隐蔽地引发死锁
LOCK IN SHARE MODE 允许多个事务同时对同一行加 S 锁(共享锁兼容),看似安全,但后续 UPDATE 需升级为 X 锁时就会暴露问题:
- 事务 A 和 B 都成功执行
SELECT ... LOCK IN SHARE MODE→ 各自持有 S 锁 - A 尝试
UPDATE→ 请求 X 锁,但被 B 的 S 锁阻塞 - B 同时尝试
UPDATE→ 请求 X 锁,但被 A 的 S 锁阻塞 - InnoDB 检测到循环等待,回滚其一
最危险的是“一切顺利”的瞬间:双方都拿到 S 锁,才开始业务判断和更新。它比 FOR UPDATE 更难定位,因为前期不阻塞,死锁发生在第二步。
真正容易被忽略的点是:死锁不是配置调优能根治的问题,而是代码里 SQL 执行顺序、索引设计、事务边界这三者共同决定的。哪怕只改一行 ORDER BY 或补一个联合索引,都可能让每分钟 5 次死锁归零。










