MySQL锁的那些复杂又让人头疼的小秘密,聊聊你不知道的锁机制和使用坑
- 问答
- 2025-12-25 22:07:21
- 1
说到MySQL的锁,很多人可能只知道个大概,比如写的时候会锁住,读的时候可能不会锁,但真要深究起来,里面的门道可多了,一不小心就会掉进坑里,导致你的应用卡死或者性能急剧下降,这些坑,往往在开发环境发现不了,一到生产环境并发量上来了,就全暴露出来了。
最经典的坑可能就是“间隙锁”(Gap Lock)了,这个锁是InnoDB在“可重复读”隔离级别下为了解决幻读问题而引入的,啥是幻读?就是你第一次读的时候发现没有某条记录,准备插入的时候,却发现别人已经插进去了,就像出现了幻觉,间隙锁锁住的就是索引记录之间的“间隙”,防止别人在这个间隙里插入新数据,你的表里有id为1, 5, 10的记录,那么间隙锁可能会锁住(1,5), (5,10), (10, +∞)这些区间,问题来了,如果你执行一个查询 SELECT * FROM table WHERE id BETWEEN 5 AND 10 FOR UPDATE;,你以为只锁住了id=5和10这两行?太天真了,它会把(5,10)这个间隙也锁住,这时候如果有人想插入id=6、7、8、9的任何一条记录,都会被卡住,直到你的事务提交,如果你的where条件是个范围查询,或者甚至是个等值查询但该值不存在(比如查id=7,但7不存在),它依然会锁住(5,10)这个间隙,这在并发插入的场景下,非常容易导致大量的锁等待和超时。(来源:MySQL官方文档关于InnoDB锁机制的章节)

另一个让人头疼的是“Next-Key Lock”,它是记录锁和间隙锁的结合体,它锁住的是索引记录本身以及它前面的间隙,这个机制的本意是好的,是为了保证更高程度的隔离性,但它的加锁规则有时候会有点“诡异”,当你的SQL使用了非唯一索引进行查询时,Next-Key Lock不仅会锁住满足条件的索引项,还会锁住这些索引项周边的间隙,更麻烦的是,它可能还会去锁住主键索引(聚簇索引),这被称为“锁提升”,如果你对索引的理解不够深,就很难预测一条SQL到底会锁住哪些范围,有时候你明明只是想锁住用户A的数据,结果因为索引设计问题,不小心把用户B的数据也给锁了,导致看似无关的两个事务发生了死锁。(来源:《高性能MySQL》一书中对InnoDB锁机制的深入探讨)
再说一个实际开发中经常遇到的坑,隐式锁”和“显式锁”的冲突,我们常用的 SELECT ... FOR UPDATE 是显式加锁,但你知道吗,在你执行INSERT操作的时候,InnoDB也会“隐式”地给新插入的这条记录加上一个排他锁,这个隐式锁在某些情况下会转换成显式锁,事务A插入了一条新记录但还没提交,此时事务B试图去查询这条记录(比如用FOR UPDATE或者用一致性读但遇到了未提交的数据),事务B会尝试给这条记录加锁,这时就会发现事务A已经有一个隐式锁了,于是事务B就会阻塞等待,同时事务A的隐式锁被提升为显式锁,这个转换过程如果和你应用代码中的显式锁混合在一起,尤其是在复杂的业务逻辑和长事务中,死锁的概率就会大大增加。(来源:MySQL官方文档关于InnoDB事务模型和锁的章节)

还有索引对锁的影响,这也是个大坑,很多人觉得锁是加在行上的,但其实锁主要是加在索引上的,如果你的SQL语句没有用到索引,或者用的不好,导致全表扫描,那可就惨了,InnoDB为了保证可重复读,会在它扫描过的所有索引记录上都加上Next-Key Lock,这相当于几乎锁住了整张表!这会导致并发性能彻底崩溃,一个不合适的查询,足以拖垮整个应用,这也是为什么DBA总是强调要优化查询,要善用索引的原因之一,不仅仅是为了查询快,更是为了锁的粒度小,不影响别人。(来源:广泛讨论于各类数据库性能优化实践文章和社区案例)
不得不提一下死锁,InnoDB有死锁检测机制,一旦发现死锁会主动回滚其中一个事务,但这只是事后补救,更可怕的是另一种情况:锁等待超时,当多个事务以不同的顺序访问和锁定资源时,不一定每次都形成环状死锁,但可能形成一条长长的等待链,比如事务A锁了行1,等待行2;事务B锁了行2,等待行3;事务C锁了行3……如果某个事务持有锁的时间特别长(比如一个慢查询或者一个忘了提交的事务),那么后面所有等待的事务都会超时失败,这种问题排查起来非常困难,因为你很难找到那个“罪魁祸首”。(来源:实际运维中的常见问题总结)
MySQL的锁机制为了平衡性能和数据一致性,设计得非常精巧但也相当复杂,不了解这些“小秘密”,就像在雷区里闭着眼走路,平时没事,一旦并发压力上来,系统怎么死的都不知道,最好的办法就是深入理解隔离级别、索引原理和具体的加锁规则,在编写SQL和设计业务逻辑时,时刻绷紧“锁”这根弦。
本文由帖慧艳于2025-12-25发表在笙亿网络策划,如有疑问,请联系我们。
本文链接:https://haoid.cn/wenda/68409.html
