什么是mysql锁_mysql锁基础概念说明

9次阅读

mysql锁锁的是索引结构,主键查询锁聚簇索引记录,二级索引查询需回表锁两级索引,无索引则全表扫描并锁每条聚簇索引记录;S锁与X锁可显式加在select上,意向锁与DDL冲突会导致表级阻塞,间隙锁防止幻读但易引发插入性能瓶颈

什么是mysql锁_mysql锁基础概念说明

MySQL锁到底锁的是什么?

MySQL锁不是直接锁“数据行”或“表”本身,而是锁在**索引结构上**——尤其是InnoDB引擎。哪怕你没建任何索引,InnoDB也会自动生成一个隐藏的聚簇索引(GEN_CLUST_INDEX),所有行锁最终都落在这个索引记录上。

  • 用主键查询(如 WHERE id = 100)→ 锁住聚簇索引中对应记录
  • 用二级索引查询(如 WHERE name = 'Alice')→ 先锁二级索引记录,再锁对应的聚簇索引记录(回表锁)
  • 没走索引(WHERE status = 'pending' 且该字段无索引)→ 全表扫描,对**每条记录的聚簇索引都加锁**,等效于锁全表

共享锁(S锁)和排他锁(X锁)怎么手动加?

默认情况下,SELECT 不加任何锁;而 UPDATE/delete/INSERT 自动加 X 锁。但你也可以显式控制读锁行为:

SELECT * FROM users WHERE id = 5 LOCK IN SHARE MODE;  -- 加 S 锁(其他事务还能读,不能写) SELECT * FROM users WHERE id = 5 for UPDATE;         -- 加 X 锁(其他事务既不能读也不能写)
  • LOCK IN SHARE MODE:适合“查后校验、不改但需防别人改”的场景,比如库存预占前确认余额
  • FOR UPDATE:适合“查-改”原子操作,比如扣款前锁定账户行,避免并发超扣
  • 两者都只在事务内生效,COMMITROLLBACK 后自动释放

为什么有时候明明只查一行,却把整张表都卡住了?

这不是锁错了,而是**意向锁(IS/IX)+ 表级操作冲突**导致的。例如:

  • 事务A执行 SELECT ... FOR UPDATE → 拿到某行X锁,同时隐式持有表级 IX
  • 事务B此时执行 ALTER table users ADD column xxx → 需要获取表级 X
  • 但表级 X 锁与已有的 IX 锁冲突 → 事务B被阻塞,看起来像“整表卡死”

这类问题在DDL频繁的环境(如开发库)特别常见。解决办法不是删锁,而是避开高峰期执行 DDL,或使用支持在线DDL的MySQL版本(如 8.0+ 的 ALgoRITHM=INSTANT)。

行锁 ≠ 安全锁:间隙锁(Gap Lock)才是幻读的幕后推手

当你执行范围查询(如 SELECT * FROM orders WHERE amount > 100 FOR UPDATE),InnoDB不仅锁住现有记录,还会锁住“间隙”——即记录之间的空档。这就是间隙锁(Gap Lock),它防止其他事务在间隙中插入新行,从而避免幻读。

  • 间隙锁不锁记录本身,只锁索引区间,比如 (100, 200) 这个开区间
  • 唯一等值查询(WHERE id = 5)且 id 是主键 → 只加记录锁(Record Lock),不加间隙锁
  • 非唯一索引或范围条件 → 默认启用间隙锁(可被 innodb_locks_unsafe_for_binlog=ON 关闭,但不推荐)

真正容易被忽略的点是:**间隙锁的存在让“看似安全”的范围查询,实际阻塞了大量插入操作,尤其在高并发写入场景下,它比行锁更容易成为性能瓶颈。**

text=ZqhQzanResources