mysql索引和锁之间有什么关系_mysql并发原理说明

MySQL行锁实际是对索引项加锁,无索引或索引失效时会升级为全表扫描并加X锁,等效锁表;InnoDB默认临键锁(记录锁+间隙锁)防幻读,READ COMMITTED可降级为仅记录锁。

mysql索引和锁之间有什么关系_mysql并发原理说明

索引没建好,行锁会自动升级成表锁

MySQL 的行锁不是“对行加锁”,而是“对索引项加锁”。InnoDB 只能通过索引定位数据,如果 WHERE 条件字段没有索引(或索引失效),优化器无法快速定位记录,就会退化为全表扫描——此时为了保证一致性,InnoDB 会为**所有扫描过的记录**加锁;而最坏情况下,它可能对**聚簇索引的每条记录都加 X 锁**,效果等同于锁表。

  • 常见错误现象:UPDATE user SET status=1 WHERE phone='138xxx' 执行极慢、阻塞其他事务,SHOW ENGINE INNODB STATUS 显示大量 lock_mode X locks rec but not gap 且涉及成千上万行
  • 实操建议:执行前先用 EXPLAIN 确认是否走了索引;对高频查询/更新的字段(如 phoneorder_no)务必建立单独索引或作为联合索引的最左前缀
  • 特别注意:LIKE '%abc'OR 混合条件、函数包裹字段(如 WHERE DATE(create_time) = '2025-01-01')都会导致索引失效,触发隐式锁表

为什么加了索引还锁不住“该锁的行”?临键锁(Next-Key Lock)才是默认行为

InnoDB 默认使用 REPEATABLE READ 隔离级别,其行锁实际是 Next-Key Lock(临键锁):即「记录锁 + 间隙锁」的组合。它不仅锁住匹配的索引记录,还会锁住该记录与前一条记录之间的「间隙」,目的是防止幻读。

  • 使用场景:执行 SELECT * FROM order WHERE amount > 100 FOR UPDATE 时,即使表中当前只有 amount=150amount=200 两条记录,InnoDB 也会锁定 (100, 150)、(150, 200)、(200, +∞) 这三个间隙,阻止其他事务插入 amount 在这些范围内的新订单
  • 参数差异:若改用 READ COMMITTED 隔离级别,InnoDB 会退化为只加「记录锁」(Record Lock),不加间隙锁——幻读风险上升,但并发写入能力提升
  • 容易踩的坑:业务以为 SELECT ... FOR UPDATE 只锁查到的几行,结果发现插入操作被莫名阻塞,根源就是临键锁锁住了“不该插的位置”

共享锁(S锁)和排他锁(X锁)的兼容性决定并发读写能否并行

锁的本质是资源访问的互斥协议。InnoDB 中,S 锁SELECT ... LOCK IN SHARE MODE)允许多个事务同时持有,但会阻塞任何 X 锁;而 X 锁SELECT ... FOR UPDATEUPDATE/DELETE)一旦加上,其他事务既不能加 S 锁 也不能加 X 锁

音剪

音剪

喜马拉雅旗下的一站式AI音频创作平台,强大的在线剪辑能力,帮你轻松创作优秀的音频作品

下载

  • 常见错误现象:两个事务先后执行 SELECT ... FOR UPDATE 查询同一行,第二个事务卡住直到第一个提交——这是正常行为;但如果两个事务都只执行 SELECT ... LOCK IN SHARE MODE,则可以并发成功
  • 实操建议:读多写少场景下,优先用 LOCK IN SHARE MODE 替代 FOR UPDATE,减少写冲突;更新前务必确认是否真需要独占锁,避免过度加锁拖慢整体吞吐
  • 性能影响:S 锁之间不互斥,开销远小于 X 锁;但大量 S 锁仍会占用锁结构内存,极端情况下触发 Lock wait timeout exceeded

MVCC 不是“不用锁”,而是让读操作尽量避开锁

MVCC(多版本并发控制)让普通 SELECT(不带 FOR UPDATELOCK IN SHARE MODE)无需加锁,直接读取事务开启时刻的快照版本。但这只对「一致性非锁定读」有效;一旦涉及修改(UPDATEDELETE)或显式加锁读,InnoDB 仍必须获取 X 锁或 S 锁来保证隔离性。

  • 关键区别SELECT * FROM user WHERE id=123 → 走 MVCC,无锁SELECT * FROM user WHERE id=123 FOR UPDATE → 必须加 X 锁,哪怕数据没变
  • 容易踩的坑:误以为“开了 MVCC 就不会锁表”,结果在高并发更新场景下,因索引缺失或临键锁范围过大,导致大量事务排队等待,监控看到 innodb_row_lock_waits 持续上涨
  • 验证方式:可通过 SELECT * FROM information_schema.INNODB_TRX 查看当前活跃事务及其锁等待状态,结合 INNODB_LOCKSINNODB_LOCK_WAITS 定位具体阻塞链

索引和锁的关系不是“有索引就安全”,而是“索引质量直接决定锁的粒度和范围”。一个没走索引的 UPDATE 可能锁全表,而一个设计不当的联合索引(比如把低区分度字段放最左)会让临键锁覆盖大片无关数据——这些细节,在压测和慢日志分析里才真正暴露出来。

https://www.php.cn/faq/2004658.html

发表回复

Your email address will not be published. Required fields are marked *