innodb行锁本质是锁索引,未命中索引或索引失效时会全扫描聚簇索引并加next-key lock,看似“锁表”;rc比rr锁更少、持有时间更短,更适合高并发更新;insert瓶颈常源于自增锁争用,应调为交错模式并批量插入;死锁多因select for update访问顺序不一致,须统一索引与顺序。

MySQL 默认隔离级别下,InnoDB 的行锁为什么还会锁表?
不是锁表,是锁索引。当查询条件未命中索引(比如 WHERE status = 'pending' 但 status 列没建索引),InnoDB 会退化为聚簇索引的全扫描,对所有扫描到的记录加 Next-Key Lock,看起来像“锁表”。更隐蔽的是,即使有索引,若使用了函数或类型隐式转换(如 WHERE DATE(create_time) = '2024-01-01'),也会导致索引失效,触发范围锁膨胀。
- 用
EXPLAIN确认type是ref/range,而非ALL或index - 避免在索引列上使用函数、
LIKE '%xxx'、IS NULL(除非该列有单独的IS NULL索引) - 执行
SELECT * FROM INFORMATION_SCHEMA.INNODB_TRX查看当前事务持有的锁范围,配合INNODB_LOCK_WAITS定位阻塞源头
READ COMMITTED 和 REPEATABLE READ 在高并发更新时的实际差异
很多人以为 REPEATABLE READ(RR)只是“可重复读”,其实它在写场景影响更大:RR 下,普通 UPDATE 语句会基于事务启动时的快照做条件判断,但加锁仍按最新版本进行;而 READ COMMITTED(RC)每次读都取最新已提交版本,锁只加在真正命中的记录上,锁持有时间更短,冲突概率更低。
- 电商库存扣减类场景,强烈建议用 RC:避免“幻读”锁住不该锁的间隙,提升并发吞吐
- RC 下
UPDATE ... WHERE id = ?只锁匹配到的行;RR 下相同语句可能额外锁住id附近的间隙(防止新插入) - 切换前确认业务能接受“非一致性读”——比如报表类逻辑依赖多次读结果一致,就不能切 RC
如何让 INSERT 不成为高并发瓶颈?
INSERT 慢不一定是磁盘 I/O,很可能是自增主键争用或唯一索引冲突检查。InnoDB 的 auto_increment 锁在高并发插入时会串行化获取值,尤其当 innodb_autoinc_lock_mode = 0(传统模式)时最严重。
- 设
innodb_autoinc_lock_mode = 2(交错模式),允许批量插入预分配 ID,大幅降低锁等待 - 避免高频单条
INSERT ... SELECT,改用批量INSERT VALUES (),(),()(最多 1000 行/批) - 如果主键非业务强相关,考虑用雪花算法生成 ID,绕过自增锁和聚集索引热点
- 唯一约束校验成本高,高频插入场景慎用多列组合唯一索引,优先用应用层幂等控制
事务里混用 SELECT 和 UPDATE 为什么容易死锁?
典型模式:SELECT ... FOR UPDATE 先查再改,但查的顺序与另一事务不一致(比如一个按 user_id 查,一个按 order_id 查),就会形成循环等待。InnoDB 死锁检测开销小,但频繁死锁本身说明访问路径设计混乱。
- 所有涉及加锁的读操作,必须按同一索引、同一顺序访问(例如固定先查
PRIMARY KEY,再查二级索引) - 避免在事务中执行不确定行数的
SELECT ... FOR UPDATE LIMIT N,N 变化会导致锁范围不可预测 - 用
SHOW ENGINE INNODB STATUS查看最近死锁详情,重点关注*** (1) WAITING FOR THIS LOCK TO BE GRANTED:后面的 SQL 和索引名
实际高并发场景中,锁行为比隔离级别更关键;很多问题表面是“事务慢”,根因是索引没走对、锁范围失控、或事务粒度太大。调试时优先看 INNODB_TRX 和执行计划,而不是直接调大 innodb_buffer_pool_size。










