MySQL行锁实际加在索引上而非物理数据行,因InnoDB通过B+树索引结构存储锁信息,主键、唯一索引、普通索引查询分别锁定对应索引项及聚簇索引记录,无索引则全表扫描并锁全表聚簇索引,导致行锁失效。

MySQL 行锁是加在索引上的,不是加在数据行上。
为什么锁的是索引,不是数据行
InnoDB 没有直接对“物理数据行”加锁的机制。它把锁信息存在 B+ 树索引结构中,通过锁定索引记录(Index Record)来间接控制对数据的并发访问。这样设计是为了避免全表扫描找锁,大幅提升性能。
- 主键查询(如
WHERE id = 100)→ 锁聚簇索引中对应的一条索引项 - 唯一索引查询(如
WHERE username = 'alice')→ 锁该唯一索引项 - 普通索引查询(如
WHERE city = 'Beijing')→ 锁该二级索引项 + 对应的聚簇索引记录 - 没索引的查询(如
WHERE remark LIKE '%error%')→ 全表扫描 → 锁聚簇索引所有记录 → 等效表锁
一个关键后果:无索引 = 行锁失效
如果 WHERE 条件字段没有索引,或者索引因函数、类型转换、LIKE 前缀通配等原因失效,InnoDB 就无法精确定位,只能遍历聚簇索引逐条加锁。哪怕只更新一行,也会锁住整张表的主键索引记录,造成严重阻塞。
Java项目代码review工具。分析Git变更+完整调用链路上下文,推断业务需求,进行多维度评分和分类汇总,生成完整PRD文档。包含细粒度Java代码审查清单(Null安全、异常处理、Streams、并发、equals/hashCode、资源管理、API设计、性能、MyBatis/ORM、事务边界、SQL/DD...
- 常见陷阱:
WHERE ABS(status) = 1、WHERE phone + '' = '138...'、WHERE name LIKE '%li' - 验证方式:用
EXPLAIN看type是否为ALL或index
不同索引类型下的加锁行为
锁的具体范围还取决于索引类型和隔离级别:
- 主键/唯一索引等值查询(命中且存在)→ 只加 Record X 或 Record S(纯行锁,不锁间隙)
- 主键/唯一索引等值查询(命中但不存在)→ 加间隙锁(Gap Lock),防止插入幻行
- 普通索引等值查询(RR 隔离级别)→ 默认加 Next-Key Lock(行锁 + 间隙锁)
- 即使
SELECT ... FOR UPDATE LIMIT 1,只要没走索引,照样锁全表
事务边界决定锁是否有效
行锁只在事务内持续有效。自动提交(autocommit=1)下,SELECT ... FOR UPDATE 执行完立刻提交,锁瞬间释放——这会让后续的 UPDATE 完全不受保护。
- 正确写法:显式开启事务
BEGIN,再执行SELECT ... FOR UPDATE和后续修改,最后COMMIT - 错误写法:两条语句分开执行,不在同一事务中

















