mysql行锁只锁索引而非物理行,因innodb行锁本质是加在索引记录上的record lock;无索引时退化为全索引加锁,等效表锁。

MySQL锁是事务并发访问数据时,为保证一致性而强制施加的访问控制机制——不是可选功能,而是InnoDB默认启用的底层保障。
行锁为什么只锁索引,不锁“数据行”本身?
因为InnoDB的行锁本质是record lock,它加在**索引记录上**,而非磁盘上的物理行。哪怕你没建任何索引,InnoDB也会生成隐藏聚簇索引(GEN_CLUST_INDEX),所有行锁都落在这棵B+树的叶子节点上。
- 用主键更新:
UPDATE user SET name='A' WHERE id=100→ 只在聚簇索引id=100处加X锁 - 用二级索引更新:
UPDATE user SET age=25 WHERE name='Tom'→ 先在name='Tom'的二级索引项加X锁,再回表到聚簇索引id=xxx加X锁(两处都要锁) - 没索引条件查询:
UPDATE user SET status=1 WHERE phone LIKE '%123%'→ 无法走索引,InnoDB退化为对**所有索引记录**加锁,实际效果≈表锁
共享锁(S)和排他锁(X)到底怎么互斥?
不是“读写不能并存”这么简单,关键是看锁类型组合是否兼容。InnoDB按标准规则判定:
-
S与S:✅ 兼容 → 多个事务可同时SELECT ... LOCK IN SHARE MODE -
S与X、X与S、X与X:❌ 不兼容 → 任一事务持有X锁,其他事务连S锁都申请不到 - 意向锁(
IS/IX)自动伴随出现:比如事务要对某行加X锁,会先在表级加IX锁,避免其他事务对整张表加X表锁冲突
常见误判:以为SELECT ... FOR UPDATE只锁查到的行——其实它还会触发gap lock或next-key lock(尤其在REPEATABLE READ下),可能锁住相邻间隙,防止幻读。
什么时候会从行锁“升级”成表锁?
不是主动升级,而是**因缺失有效索引被迫全表扫描+全索引加锁**,导致事实上的表级阻塞。典型场景包括:
- WHERE条件字段无索引,且执行
UPDATE/DELETE(InnoDB必须遍历所有索引记录,逐个加X锁) - 显式使用
LOCK TABLES t1 WRITE(MyISAM常用,InnoDB中应尽量避免) - 隐式MDL锁竞争:当一个长事务正在执行
SELECT,另一个线程试图ALTER TABLE,后者会被阻塞在MDL写锁上——这不是数据锁,但效果类似表不可写
验证是否发生表级争用:SHOW STATUS LIKE 'Table_locks_waited'值持续上升,说明有大量线程在等表锁。
真正容易被忽略的是:锁的生命周期绑定事务,COMMIT或ROLLBACK才释放;而MVCC只解决“读不加锁”,一旦涉及修改(哪怕只是SELECT ... FOR UPDATE),锁就立刻生效——别以为开了READ COMMITTED就能绕过锁冲突。










