MySQL InnoDB加锁读关联表时未选取关联列是否会锁定关联行
结论
SELECT 子句指定返回的列完全不会影响 FOR UPDATE 的加锁范围,你给出的关联查询语句依然会对匹配条件的父表行加锁。
原理说明
你对官方文档的第三种解读是正确的:「结果集准入条件」仅指代 JOIN ON、WHERE、HAVING 等过滤规则,和 SELECT 子句选择哪些列没有任何关系,原因如下:
- MySQL的查询执行顺序为:
FROM/JOIN 表加载→WHERE 条件过滤→SELECT 列裁剪→其他后续处理,而行锁是在扫描、过滤行的阶段就已经加上,远早于SELECT列的裁剪步骤,后续是否返回父表列完全不会影响前面的加锁逻辑。 - 你的查询执行过程中,会先扫描匹配到符合
Parent.id = ?的父表行、符合Child.id = ?的子表行,再校验JOIN关联条件,所有满足过滤和关联条件的行都属于符合结果集准入条件的行,不会被提前释放锁,和是否在SELECT中返回无关。
验证方式
你可以通过简单的并发测试确认加锁行为:
- 开启会话1,执行你给出的关联查询语句,执行后不提交事务
- 开启会话2,执行针对对应父表ID的加锁查询
SELECT * FROM Parent WHERE id = ? FOR UPDATE - 可以观察到会话2的查询会进入阻塞状态,即可证明会话1已经给对应的父表行加上了排他锁。
优化建议
如果你的场景只需要校验父子路径合法性、不需要读取父表的其他字段,同时不想给父表行加锁,可以直接简化查询语句,利用外键约束的特性(子表的parent_id如果存在关联值,必然对应存在父表行),改写为仅查子表的语句,锁范围更小、执行效率更高:
SELECT Child.* FROM Child WHERE Child.parent_id = ? AND Child.id = ? FOR UPDATE
该语句只会给匹配到的子表行加排他锁,不会触发父表的加锁逻辑。
内容的提问来源于stack exchange,提问作者R. Agnese
相关产品推荐
相关产品推荐

