# mysql数据库锁

# 大局观

你要锁东西是把整个房子锁住?还是只锁一个房间?或者干脆就锁一个抽屉呢?这就是锁的力度。——也是锁的范围大小。

全局锁:锁住整个库。命令:ftwrl

表锁:锁住整张表。特点:开销小、并发量低。引擎:MySam

行锁:锁住单行的数据。特点:并发高、可能死锁。引擎:innoDB

最大、最恨的就是全局锁。就是相当于把整个数据库锁了。(这个大楼给封了)一个命令下来整个库都别想写数据了,只能够读。这个玩意有点霸道。什么时候用呢?答:当我要做全库备份的时候就得保证数据一动不动,绝对一直性。这个时候就要使用全局锁。

少一级的是表锁。相当于锁了一个房间。像老一点的MySamy引擎用的就是表锁。好处:简单直接、开销小。坏处:只要有一个人在房间里面操作其它人都得在门口排队。并发能力就特别差。

最后就是咱们最常用的行锁。只是锁一个抽屉。这个是InnoDB引擎的强项。

  • 好处:并发高(你改你的数据,我改我的数据。只要不是同一行咱两就互不影响。)

但是你想管理一堆抽屉的钥匙是不是比管一把大门钥匙要麻烦很多。所以开销会更大。而且一不小心你拿了我的钥匙,我拿着你的钥匙二个人就卡住不动了。这个就是死锁。

总结:没有那个是完美的,用那种锁实际上就是一种取舍。

# 用锁的二种派别

  • 悲观锁
  • 乐观锁

我们平时写代码到底是用乐观锁还是悲观锁?

什么是悲观锁?

答:人如其名,它就是一个悲观主义者。他总是觉得只要我改了数据,一定就有人更我抢。所以他在干活前先用select for update这样的语句把数据给锁的死死的。谁也别想访问。在深入想一想?这个应用在什么地方最合适呢?

  • 秒杀
  • 抢库存

并发高的场景。也就是并发写操作非常的多。冲突的概率非常大。必须得先下手为强。

什么是乐观锁?

答:人如其名,它就是一个乐观主义者。总是觉得世界如此美好。没有那么多人跟我抢所以他不加锁。他直接去操作数据,数据只是在最后提交的时候用版本号或者是时间戳来对比一下。看看我干活这个一段时间内有没有人动过我的数据。如果有人改了那么这次提交就失败了。这种方式呢特别适合像改文章啊。改商品信息这类读多写少的场景。因为它省去了加锁的开销。性能上好。

  • 改文章
  • 改商品信息

# 锁自带的二种基本类型

共享锁和排他锁

这个就比较好理解:就像我们去图书馆看书一样的。

什么是共享锁?

答:共享锁也叫s锁(share锁)或者读锁。它就是等于大家一起看书。一本书可以被很多人同时看。只要大家不在上面写字就行。这就是读读共享。

场景1:这个时候有一个人想来改书上内容是一定不允的。这也就是读写互斥。

在mysql中加读锁语句如下:

实现:`lock in share mode`
1

什么是排他锁?

答:排他锁也叫x锁或者叫写锁。它就相当于我要写的这本书。

情况1:我想去修改书的内容时我必须把这本书拿走。自己一个进行修改。在我改完还回来之前这一段时间内。别人既不能看(不能看=读数据)、也不能改(不能读怎么进行修改)。——这个就是写写互斥和读读互斥。

在mysql中加写锁语句如下:

实现:`for update`
1

# innoDB中的意向锁

意向锁(Intention Lock)

意向锁是什么?怎么理解这个呢?

答:你想象一个挂在表门口的通知牌。这样就比较好理解。它的作用就是为了提高效率。

如果事务a锁了表里面某一行数据。这个时候事务b它想给整个表加一个锁。mysql怎么做呢?难道一行一行的去检查有没有行锁吗?哪表里面如果有上千万行的数据查到猴年马月去。这时候数据库就会被卡死。所以innoDB就设计了通知牌。当事务a给某一行加行锁的时候innoDB会自动在这个表的门口挂上一个牌子上面写着:里面有人正在操作,请注意。当事务b想来锁整张表的时候抬头一看这个表的门口有一个牌子。立刻就知道里面有行锁,有冲突。自己就等着。这样就提高效率。

所以:意向锁锁本身它就是一个表级锁。但是它不是用来锁数据的,而是用来打配合提高效率的。

有了意向锁这个帮手。在看看innoDB真正在一线干活的几个核心武器。

第一个:记录锁(Intention Lock)

目标:锁定索引记录本身。

触发:通过唯一索引(组件、唯一id)进行等值查询时。

select * from t where id = 10 for update
1

效果:只会锁定id =10 这行,不影响其它行。

解释:记录锁就是最纯粹的行锁。指哪就打哪。只锁定你查询命中的那一条记录。比如你用主键id = 10去更新,那么它就会只锁id=10这一行数据。这个是我们最希望看到的最理想结果。但是现实那样怎么理想?如果你的查询条件不是唯一的呢?比如说我要查age大于18这是一个范围查询。这个时候面试必考的知识点就来了。幻读。

怎么解决幻读?

答:innoDB的武器间隙锁和临键锁。

间隙锁(Gap Lock):锁定的是一个开区间(它只是锁住记录与记录之间的那个缝,比如说索引里面10和20。那么它就是锁住10到20这区间,让你没法在这个缝隙插入新的数据。它就像给数据之间画了一个结界。任何一个数据都不允许冒出来),不锁记录本身。防止间隙中插入数据。

临键锁(Next-Key Lock):记录锁 + 间隙锁的组合,锁定一个左开右闭的区间。

核心目的:在 repeatable read 隔离级别下,防止幻读。

临键锁是什么?

答:记住:记录锁 + 间隙锁的组合。

锁的是记录本身还把这条记录的那个缝也给锁住了。这个才是innoDb在可重复读隔离级别下默认使用的锁。它就是像一张大网把记录和它旁边这个缝啊全部罩住。让幻读无处遁形。

在想一想为什么有了mvcc还需要锁来防止幻读问题?

答:mvcc保证了你读的时候看不到幻影。而临键锁保证了在别人写的时候罩不住幻影。一个防读,一个防写。

双剑合璧才能天下无敌。

# 总结锁的艺术

锁这个东西没有好与坏。全局锁虽然暴力,但是在备份的时候离不开它,行锁虽然精细但你得承担死锁的风险。

临键锁虽然能够解决幻读,但是它也可能锁住没有必要的范围牺牲了一些性能。

目的:当我们面对复杂的业务场景时我们能有底气做出最合适的选择。这个一种权衡的艺术。是在数据的一致性、系统的性能、业务的并发能力之间找到这个最佳平衡点(不是最佳极值点)的能力。

# 基石

锁是并发控制的基石。没有最好的锁,只有最合适的场景。理解锁的粒度、模式与算法,是成为MySql高手的必须经历之路。

致谢

代码的世界里,我们用锁守护数据的秩序;正如现实生活中,我们用规则维护社会的公平。

Last Updated: 4/3/2026, 6:47:37 AM