不同WHERE条件的SELECT FOR UPDATE锁定同一行是否会引发死锁?
问题分析与解答
是否会造成死锁?
这种场景大概率不会产生死锁,核心原因是两个线程最终请求的是同一行数据的排他锁(X锁),无法形成死锁必需的循环等待条件:
- 若
id和unique_id都是有效索引:- 若线程T1先执行
select ... where id=1 for update,会直接定位目标行并加X锁;此时T2执行select ... where unique_id="x" for update会因目标行已被锁而阻塞,直到T1提交释放锁后才能继续,不存在循环等待。 - 反过来,若T2先获取锁,T1会被阻塞,同样不会触发死锁。
- 若线程T1先执行
- 若
unique_id无索引:
T2执行select ... for update会触发全表扫描,过程中临时对扫描到的行加锁,最终仅保留目标行的锁。这种情况下要么T1先锁目标行导致T2阻塞,要么T2先锁目标行导致T1阻塞,依然不会形成循环等待,因此不会死锁。
不同WHERE条件的潜在问题
虽不会死锁,但用不同WHERE条件操作同一行确实存在风险:
- 锁范围过大(无索引时):如果
unique_id未建索引,T2的select ... for update会全表扫描,过程中对大量无关行加锁,增加锁冲突概率,降低并发性能。 - 更新时的锁验证异常:你测试中遇到的更新问题,可能是
unique_id索引失效导致T2的select ... for update未正确锁定目标行,或是T2等待锁期间,其他事务修改了该行数据,导致update where id=1的实际修改行数与预期不符。 - 业务一致性风险:若后续表结构或数据变更,导致
id=1和unique_id="x"不再指向同一行,这种条件不一致的操作会直接破坏业务数据的一致性。
优化建议
- 确保
id和unique_id都建立有效索引(至少给unique_id建唯一索引),避免全表扫描引发的锁范围扩大。 - 尽量统一操作同一行的WHERE条件,比如始终用
id作为查询和更新的依据,减少条件不一致带来的隐患。 - 若必须使用不同条件,可在事务中先通过一个条件锁定行,再用另一个条件验证行的一致性(比如查询后确认
id和unique_id符合预期)。
内容的提问来源于stack exchange,提问作者user2130460
相关产品推荐
相关产品推荐

