死锁主因是多事务对同一组资源加锁顺序不一致,而非select for update本身;须严格按固定顺序(如字母序或业务流)访问表,确保所有for update走主键或唯一索引,并将业务判断下推至where条件。

SELECT FOR UPDATE 死锁根本不是锁太强,是加锁顺序乱了
死锁几乎从不因为“用了 FOR UPDATE”,而在于多个事务对同一组资源的加锁顺序不一致。比如事务 A 先锁 order 再锁 user,事务 B 反过来,InnoDB 在加锁过程中就形成环路,MySQL 只能选一个回滚(报错 Deadlock found when trying to get lock)。
这种问题和索引是否建好、隔离级别设多高关系不大,真正要动的是代码里 SQL 的执行顺序。
- 所有涉及多表的
SELECT FOR UPDATE或UPDATE,必须按**固定顺序**访问表:要么严格按表名字母序(order_item→order→user),要么按业务主次(如订单流必须是order→order_item→payment) - 禁止用 if/else 动态拼接 SQL 来决定先查哪张表,例如不能写
if (type == 'refund') { SELECT ... FROM user FOR UPDATE; SELECT ... FROM order FOR UPDATE; } - 用
EXPLAIN FORMAT=TRADITIONAL确认每张表的SELECT FOR UPDATE都走了**主键或唯一索引**;否则可能锁整段索引区间,放大冲突面
别在 FOR UPDATE 后再做业务判断,把逻辑压进 WHERE
常见错误模式:SELECT * FROM order WHERE status = 'pending' FOR UPDATE 查出 100 行,再在 PHP/Java 里循环判断哪些能处理——这会让锁一直挂着,直到整个循环结束,期间其他事务完全无法碰这些行。
更糟的是,不同事务可能以不同顺序遍历结果集(比如没加 ORDER BY id),导致加锁顺序天然不一致,死锁概率飙升。
- 把判断条件全部下推到
WHERE子句中,例如改用SELECT id FROM order WHERE status = 'pending' AND created_at - 如果条件复杂(如需关联用户状态),优先走覆盖索引+主键回查,而不是在应用层拼逻辑
- 避免
SELECT * FOR UPDATE,只查必要字段,减少网络传输和事务时间,间接缩短锁持有窗口
高频争抢场景下,用 INSERT ON DUPLICATE KEY UPDATE 替代“先查后更”
当多个事务频繁尝试更新同一业务键(如订单号 order_no),用 SELECT FOR UPDATE + UPDATE 组合极易死锁:两个事务都查到同一条记录,都想升级为 X 锁,但各自又可能持有别的行锁,形成闭环。
INSERT ... ON DUPLICATE KEY UPDATE 虽然内部也加锁,但它是一条原子语句,InnoDB 对冲突行的加锁路径更可控,且省掉一次查询开销。
- 确保
ON DUPLICATE KEY的列有**唯一索引或主键**,否则语句会退化为全表扫描 - 注意它的锁行为:会对插入意向间隙锁 + 唯一键冲突行的记录锁同时加锁,仍需避免和其他 DML 混用
- 若业务允许,可用 Redis 分布式锁对同一
order_no请求做轻量串行化,把数据库压力前置消化
事务边界必须收缩到“读–算–写”,非 DB 操作一律移出
一个 Web 请求里开事务、调第三方 API、写日志、再更新 DB,这种写法等于让 MySQL 为 HTTP 延迟背锅。哪怕只是 300ms 的外部响应,也会让锁白白挂着,死锁概率指数上升。
真正该留在事务里的,只有三步:用 SELECT FOR UPDATE 读数据 → 本地做计算或校验 → 执行 UPDATE 或 INSERT。
- HTTP 调用、Redis 读写、文件生成、
sleep()、同步日志落盘……全部挪到COMMIT之后 - 批量操作别用循环单条
UPDATE,改用UPDATE ... WHERE id IN (...)或INSERT ... VALUES (), (), () ON DUPLICATE KEY UPDATE - 调试时加的日志不要留在线上,尤其不能放在事务块内写磁盘日志——它可能比 SQL 还慢
最容易被忽略的一点:死锁日志里显示的“victim”事务,往往不是问题代码,而是那个最先触发冲突的无辜请求。真正要盯的,是监控中反复出现的 Lock wait timeout exceeded 和高频 Deadlock found,它们指向的是加锁顺序混乱或非 DB 操作滞留事务内的老代码。











