rr隔离级别下,无索引的select...for update实际锁全表,因innodb需全表扫描并对每行加next-key锁;有索引时则按索引类型决定锁粒度:唯一索引等值查询只锁单行,普通索引或范围查询会锁记录及间隙。

RR 隔离级别下,索引不是“影响”行锁范围,而是直接决定锁什么、锁多大范围。没有索引,SELECT ... FOR UPDATE 就不是行锁,而是表锁。
FOR UPDATE 没走索引时一定锁全表
RR 下,InnoDB 的行锁本质是加在索引上的。如果 WHERE 条件字段没索引,优化器无法定位具体索引项,只能扫描聚簇索引(即整张表)——此时 InnoDB 会退化为对每个主键记录加锁,再叠加间隙锁覆盖所有间隙,最终效果等同于锁整个表。
- 常见错误现象:
SELECT * FROM user WHERE name = 'alice' FOR UPDATE执行极慢,且阻塞其他事务对任意user行的修改 - 使用场景:线上排查发现某条
FOR UPDATE语句引发大面积阻塞,先查EXPLAIN看是否type=ALL - 参数差异:该行为只在
RR下发生;RC下即使没索引也只锁命中的行(不加间隙锁),但依然性能差 - 性能影响:全表扫描 + 全表加锁 → CPU、IO、锁等待三重压力,
Innodb_row_lock_waits和Innodb_row_lock_time_avg会明显升高
唯一索引等值查询只锁单行,但缺失值会锁间隙
id 是主键或唯一索引时,SELECT ... WHERE id = 100 FOR UPDATE 只对索引项 100 加 X,REC_NOT_GAP 锁(纯记录锁),不涉及间隙。
但若查不到:
SELECT ... WHERE id = 101 FOR UPDATE(101不存在)实际锁定的是
(100, 200]这个 Next-Key 范围(假设前后相邻主键是100和200)目的是防止其他事务插入
id=101,解决幻读容易踩的坑:以为“没查到就不锁”,结果发现
INSERT INTO ... VALUES (101, ...)被卡住注意:这个间隙锁同时作用于主键索引和对应唯一索引(如有),但不会扩散到其他普通索引
MySQL(Linux)下载MySQL 9.6.0是面向Linux平台的2026年创新版本,核心架构迎来重大革新。其将外键约束与级联操作从InnoDB引擎层上移至SQL层,确保所有数据变更均被完整记录至Binlog,彻底解决了CDC(变更数据捕获)与主从复制中的数据不一致难题。此外,该版本引入container_aware启动选项以原生适配容器环境,并对审计日志进行了组件化重构,为追求极致数据一致性与云原生体验的开发者提供了全新选择。
普通索引等值查询会锁多个记录+间隙
name 是普通索引(非唯一),SELECT ... WHERE name = 'bob' FOR UPDATE:
对每个
name='bob'对应的索引项加记录锁同时对这些索引项之间的间隙加
GAP锁(例如('bob', 'carol'))还会向右滑动,找到第一个不等于
'bob'的值(比如'carol'),对其加 Next-Key 锁,覆盖('bob', 'carol']示例:数据中
name值为['alice', 'bob', 'bob', 'carol'],则锁范围实际包含('alice', 'bob']和('bob', 'carol']关键点:普通索引的等值查询不会退化为纯记录锁,哪怕只命中一行,也会带间隙锁
性能影响:比唯一索引更易引发锁冲突,尤其在高并发写入相同
name值时
真正容易被忽略的是:锁范围不取决于你写了什么 WHERE 条件,而取决于 MySQL 实际走哪个索引、以及该索引的类型和数据分布。看执行计划只是第一步,还得结合 performance_schema.data_locks 查实时锁状态,才能确认到底锁了哪几段。










