> 文档中心 > 深入浅出,详解MySql中的各种锁

深入浅出,详解MySql中的各种锁

了解MySQL中的锁机制不仅能帮助我们通过面试,还能提升我们开发数据库应用的能力。锁是保障数据安全和一致性的关键工具,根据不同的操作和场景, MySQL 提供了多种锁类型和策略。以下是对MySQL锁机制的简要概述和实际应用案例。

为什么需要锁?

正如我们在学校图书馆自习时,需要保证自己正在阅读的书籍不被他人随意修改一样,数据库中的锁确保了在并发环境下数据的正确性和一致性。假设多个用户同时试图修改同一条用户记录,没有锁机制的话,数据可能会变得混乱,出现不一致的情况。因此,锁机制应运而生,以防止这种情况的发生。

锁的类型

读锁(共享锁)和写锁(排他锁)

如果我们将共享锁比作多人同时阅读同一本书,那么排他锁就如同有人正在修改这本书,期间其他人不能进行任何修改,只能等待作者完成。例如,当事务A正在更新一条用户记录时,事务B试图更新同样的记录会被阻塞,直到事务A提交或回滚。

间隙锁、临键锁和插入意向锁

间隙锁用于防止“幻读”,即在一个事务中两次读取数据时,期间其他事务插入了新的数据。间隙锁会在数据间隙上加锁,防止这种情况发生。不过,这也可能导致死锁的潜在风险。比如,事务A在查询并加锁某个不存在的数据间隙后,事务B和C试图插入数据到同一间隙,就会被阻塞,形成一个等待链,导致死锁。

悲观锁和乐观锁

悲观锁假设数据会被频繁修改,因此会在读取或写入时总是先加锁。这就像一个谨慎的司机,总是提前刹车以防突发情况。乐观锁则认为数据不常被修改,只在提交时检查是否有冲突。这就像一个自信的骑行者,认为路况良好,不需要频繁减速。

案例分析

假设我们有一个用户表,事务A试图更新某个用户的信息,而事务B试图插入一个新用户。如果事务A加了排他锁,事务B就必须等待,这说明了排他锁的作用。另一方面,如果事务C试图在事务A完成前读取该记录,它会共享锁,允许同时读取而不阻塞,这体现了共享锁的优势。间隙锁的应用则体现在,事务A查询了一个不存在的记录后,其他事务B和C尝试插入到附近的间隙就会被阻塞,避免了幻读的问题。

整体而言,掌握锁机制不仅是技术上的提升,更是对数据库并发控制的深刻理解,帮助我们编写出更加高效和可靠的数据库应用。锁的选择和管理将直接影响应用的性能和稳定性,合理运用这些机制,可以显著提升数据库操作的效率。

深入浅出,详解MySql中的各种锁

    • 为什么需要锁?
    • 1 对数据操作的类型划分
      • 1.1 读锁(共享/S锁)
      • 1.2 写锁(排他/X锁)
      • 案例
    • 2 锁粒度划分
      • 2.1 表锁
      • 2.2 行锁
        • 2.2.1 记录锁
          • 案例
        • 2.2.2 间隙锁
          • 案例
        • 2.2.3 临键锁
        • 2.2.4 插入意向锁
    • 3 锁态度划分
      • 3.1 悲观锁
      • 3.2 乐观锁
    • 4 全局锁和死锁
      • 4.1 全局锁
      • 4.2 死锁

其实对于一个开发人员来说,对数据库的掌握并不需要这么深入。
但本人亲身经历,面试的时候来一句“请你说说什么是间隙锁,一瞬间将我排除在候选人名单外。
而我面试的仅仅是中级开发工程师,可见现在的内卷程度有多严重。

因此本文章整理了一番Mysql中各种锁的作用和原理,欢迎学习交流。
🍅你的关注和点赞对我很重要🍅

为什么需要锁?

我们知道Mysql数据库提供了4种隔离级别来预防我们操作数据的时候可能带来的并发事务问题
深入浅出,详解MySql中的各种锁
上面的图片相信每个人都烂熟于心了,Mysql默认的隔离级别就是RR(REPEATABLE READ),避免了脏读和不可重复读,我们一般也将这个隔离级别成为:可重复读。

当面试进行到这里,你自我感觉还很良好的时候,面试官突然问你:你知道数据库的隔离级别的原理是什么吗?

好,这个时候,数据库的概念就出来了。

下面会通过这个脑图的结构依次简绍:
在这里插入图片描述

1 对数据操作的类型划分

1.1 读锁(共享/S锁)

读锁也称共享锁,多个事务同时读取同一份数据,相互不会影响不会阻塞,数据安全。
给select语句加共享锁

Select * from test LOCK IN SHARE MODE;Select * from test FOR SHARE;

1.2 写锁(排他/X锁)

写锁也称排他锁,排他排他,顾名思义就是排挤他人。多个事务同时操作(有读有写或同时写)同一份数据,在一个事务还没完成(提交)前,它会阻断其他的事务,保证数据的安全性。
给select语句加排他锁

Select * from test FOR UPDATE;

update语句默认会加上排他锁
看到这里你会不会有点感觉了。脏读是不是就是通过排他锁解决的呢?

案例

有一张user表,其中有这样一行初始数据:
深入浅出,详解MySql中的各种锁
现在模拟两个事务对这行数据进行操作:

  • 事务1:
    查询user表,加排他锁
    深入浅出,详解MySql中的各种锁
  • 事务2:
    查询user表,加排他锁
    在这里插入图片描述

2 锁粒度划分

2.1 表锁

表锁这里我们不展开解释了。顾名思义就是每次加锁就把整张表给锁了,一杆子打死。
我们知道Mysql有两种引擎,InnoDB和MyIsam。我们常用的是InnoDB,因为MyIsam不支持事务,并且MyIsam只能表锁,不能行锁。 而InnoDB支持事务,并且可以同时支持表锁行锁

2.2 行锁

为了提高数据库的并发吞吐量,每次锁定的范围越小越好,但这样的话加锁的数量就会变多,在锁的管理上也是很消耗资源的一件事。
所以,什么时候用表锁,什么时候用行锁,需要我们结合实际情况来考虑。
这里我们就先详细了解下我们平时用的最多的行锁

2.2.1 记录锁

记录锁记录锁,顾名思义就是给某一条记录加锁。
对于记录锁而言,它也分为共享锁排他锁,功能参考1.1和1.2的描述。

案例

有一张user表,其中有这样一行初始数据:
深入浅出,详解MySql中的各种锁
现在模拟两个事务对这行数据进行操作:

  • 事务1:
    修改user表,把张三的年龄改为20,默认加记录锁(排他锁,行锁)
    深入浅出,详解MySql中的各种锁
  • 事务2:
    查询user表,加读锁
    深入浅出,详解MySql中的各种锁

2.2.2 间隙锁

间隙锁是面试种最常问到的东西,听名字感觉很高大上的,其实也是很简单的一个东西。
间隙锁是防止幻读而产生的一种锁,是加在某一条不存在的数据上的一个锁。
幻读就是事务A第一次读到8条数据,这时候事务B插入了一条,事务A这时第二次读,发现读出来9条数据。多出来的这一条我们成为幻影数据

案例

初始数据:
深入浅出,详解MySql中的各种锁

间隙锁的案例我就不截图来演示,用文字描述更易懂。

  1. 事务A查询id =8的数据,发现查不到数据,并加了读锁,未提交。
  2. 事务B现在要插入一个id为6或7或8或9的数据,不能插入。
  3. 事务A提交了事务。
  4. 事务B现在才插入成功。

很明显,解决了幻读的问题。那到底是什么原理呢?为什么事务B不能插入呢?

事务A查询了一个不存在数据,例如id=8的数据。那么就会产生间隙锁间隙锁的这个间隙就会是5——10,也就是当前查寻Id的前后两个数据,id为5和id为10之间不能插入任何数据。

如果事务A查询的id=15,那么根据间隙锁的原理,插入的数据id如果大于当前表最大的id,则不能插入。这里就是如果事务B要插入id大于10的数据就插入失败。

间隙锁的危害:间隙锁是有可能会造成死锁的问题。

2.2.3 临键锁

临键锁就是记录锁间隙锁的结合,把间隙两头的数据也加上记录锁
结合上面的例子,就是:
事务A查询一个id=8的数据,那么5——10区间就会加上间隙锁,不能插入数据。而5和10这两条数据也会加上记录锁,不能读写操作。

具体操作就是在事务A进行范围查询的时候,加上for update。

2.2.4 插入意向锁

回顾之前我们的间隙锁,被间隙锁锁定的数据是阻塞、等待。
如果现在有多个插入事务都阻塞了,那他们之间会生成一个内部锁:插入意向锁

如:

  1. 事务A查询id =8的数据,发现查不到数据,并加了读锁,未提交。(5-10之间产生间隙锁
  2. 事务B插入一个id为6的数据。阻塞。
  3. 事务C插入一个id为7的数据。阻塞。
  4. 事务B,C之间就会产生一个插入意向锁。如果B、C插入的数据不冲突,那待事务A释