MySQL间隙锁详解:并发世界的"隐形守卫"
骚话王又来分享知识了!今天咱们聊聊MySQL中让无数开发者"闻风丧胆"的间隙锁(Gap Lock)。如果你觉得有用,记得点赞收藏,后续还有更多数据库骚操作等你来挖掘!
间隙锁是什么?
间隙锁,顾名思义,就是锁住"间隙"——不是锁住具体的某一行数据,而是锁住两行数据之间的区间。它是InnoDB在可重复读(REPEATABLE READ)和串行化(SERIALIZABLE)隔离级别下,为了防止幻读而引入的一种锁。
打个比方,间隙锁就像是小区门口的保安,不管你是不是住户,只要你想往里加人(插入新数据),保安就会拦住你,确保小区里的人数不会突然多出"幻影"。
间隙锁的应用场景
-
防止幻读
在可重复读隔离级别下,事务A查到某区间没有数据,事务B不能在A提交前往这个区间插入新数据,否则A再次查询时就会"见鬼"——这就是幻读。间隙锁正是用来防止这种情况的。 -
范围查询加锁
比如:SELECT * FROM user WHERE age > 20 AND age < 30 FOR UPDATE;这条语句会锁住age在(20,30)区间的所有"间隙",防止其他事务在这个区间插入新数据。
间隙锁的类型
- 纯间隙锁(Gap Lock):只锁住区间,不锁具体行。
- 临键锁(Next-Key Lock):锁住区间+区间右端点的行(InnoDB默认的实现方式)。
- 行锁(Record Lock):只锁住具体的某一行。
实际开发中,InnoDB大多数时候用的是"临键锁",即"间隙+行"一起锁,既防止幻读,又保证数据一致性。
实战举例
例1:防止插入"幻影"数据
假设user表里有age=21和age=29两条数据。
-- 事务A
START TRANSACTION;
SELECT * FROM user WHERE age > 20 AND age < 30 FOR UPDATE;
-- 此时会锁住(20,21)、(21,29)、(29,30)这几个间隙
这时,事务B想插入一条age=25的数据:
-- 事务B
INSERT INTO user(age) VALUES(25);
-- 被阻塞,直到事务A提交
例2:删除和更新也会触发间隙锁
DELETE FROM user WHERE age > 20 AND age < 30;
-- 也会锁住对应的间隙,防止其他事务插入新数据
间隙锁的兼容性与注意事项
- 只在可重复读及以上隔离级别生效,读已提交(READ COMMITTED)下不会加间隙锁。
- 只影响插入操作,不会阻塞对已存在行的更新和删除。
- 范围越大,锁的间隙越多,并发性能可能受影响,设计SQL时要注意锁粒度。
开发者视角:间隙锁的那些坑
- 死锁风险:多个事务同时锁不同区间,容易互相等待,死锁频发。
- 性能瓶颈:大范围加锁,插入被阻塞,吞吐量骤降。
- 误用场景:有些业务其实不需要防幻读,结果却被间隙锁拖慢了速度。
建议:能用唯一索引锁定具体行就别用范围锁,业务允许的话,隔离级别降到READ COMMITTED,间隙锁就不会"作妖"了。
间隙锁与MVCC:各司其职,强强联手
很多同学会疑惑,InnoDB不是有MVCC(多版本并发控制)了吗?为啥还要搞个间隙锁,两者到底解决什么问题,能不能只用一个?
MVCC:让读操作"各看各的"
MVCC的本质,是让每个事务看到自己"时空"里的数据快照。这样,读操作不会被写操作阻塞,大家互不干扰,极大提升了并发性能。
比喻一下,MVCC就像是每个人都戴着自己的"AR眼镜",看到的世界都是自己那一刻的样子,别人怎么改都不影响你。
间隙锁:让写操作"井水不犯河水"
MVCC虽然能解决读写冲突,但对"幻读"无能为力。比如你查某个区间没数据,别人趁你不注意插入一条新数据,你再查就"见鬼"了。间隙锁就是专门防止这种"幻影"插入的。
间隙锁就像是给小区大门上锁,防止有人趁你不注意偷偷溜进来。
为什么需要共存?
- MVCC让读操作高并发、低延迟,但无法防止幻读。
- 间隙锁专治幻读,但如果没有MVCC,读写就会频繁互相阻塞,性能大打折扣。
所以,MVCC和间隙锁是InnoDB并发控制的"双保险":
- MVCC让你"放心读",
- 间隙锁让你"放心写",
- 两者配合,既保证了性能,又守住了数据一致性。
这就像是左手安全带,右手刹车,缺一不可!
为什么MVCC不能单独解决幻读?
MVCC的本质,是让每个事务读到自己"时空"里的数据快照。这样,普通的"不可重复读"问题(比如你查一行数据,别人改了,你再查还能看到原来的版本)确实能靠MVCC搞定。
但幻读是另一回事。幻读指的是:
- 你第一次查某个范围,没有符合条件的数据;
- 别的事务偷偷插入了新数据在这个范围里;
- 你再查,突然多出一条"幻影"数据。
MVCC只能保证你读到的"老数据"不会变,但新插入的数据本来就不在你的快照里。如果你是"快照读"(普通SELECT),你确实看不到"幻影";但如果你是"当前读"(比如加锁的SELECT ... FOR UPDATE),InnoDB就必须用间隙锁来防止别人插入新数据,否则你还是会遇到幻读!
- MVCC能防止不可重复读,但防不了幻读。
- 防止幻读,必须靠间隙锁等加锁机制。
所以,MVCC和间隙锁是"左手安全带,右手刹车",缺一不可!
间隙锁虽"隐形",却是并发世界的守卫者。理解它、善用它,才能在高并发场景下游刃有余,避免死锁。骚话王又来送干货,如果觉得有用就点赞收藏,咱们下篇见!