MySQL锁机制

71 阅读6分钟

一.概述

锁是计算机协调多个进程或线程并发访问某一资的机制。在数据库中,传统的计算机资源(CPU、RAM、I/O)的争用以外,数据也是一种供许多用户共享的资源。如何保证数据并发访问的一致性、有效性是所有数据库必须解决的一个问题,锁冲突也是影响数据库并发访问性能的一个重要因素。从这个角度来说,锁对数据库而言显得尤其重要,也更加复杂。

例子:

比如车票的售卖,系统里的票数只有一张,这时如果不止一个人同时购买,那么如何解决票的归属问题?

这里我们肯定是要用到事务操作,取出票的数量,对票的数量进行更新,整个过程,可以使用锁对有限的资源进行保护,解决隔离和并发的矛盾

分类:

  • 全局锁: 锁定数据库中的所有表
  • 表级锁: 每次操作锁住整张表
  • 行级锁: 每次操作锁住对应的行数据

二.全局锁

全局锁就是对整个数据库实例加锁,加锁后整个实例就处于只读状态,后续的DML语句,DDL语句,以及更新操作的事务提交语句都将被阻塞。

全局锁使用的场景是做全库的逻辑备份,对所有表进行锁定,从而获取一致的视图,保证数据的完整性

为数据库加上全局锁: flush tables with read lock;

解除全局锁: unlock tables;

三.表级锁

表级锁,每次操作锁住整张表。锁定粒度大,发生锁冲突的概率最高,并发度最低。应用在MyISAM、InnoDB、BDB等存储引擎中。

分类:

  • 表锁
  • 元数据锁
  • 意向锁

1.表锁

分类:

  • 表共享读锁(read lock)
  • 表独占写锁(write lock)

语法:

1.加锁:lock tables 表名... read/write

2.释放锁:unlock tables / 客户端直接断开连接

接下来我们先来看看读锁 image.png

简单来说就是,加了读锁后,只能进行读操作。

image.png

我们可以发现,加上表锁之后,不影响两个客户端的读操作,但是都无法进行写操作,加锁的一方想要写操作时,会报错,而另一个客户端想进行写操作时,会阻塞直到这个表锁被释放

image.png

同样的我们再来分析一下写锁

image.png

简单来说,加了写锁后,其他客户端不能进行读写操作,而当前客户端既可以进行读操作也可以进行写操作

image.png

我们可以发现在加了读锁后当前客户端既可以进行读取也可以进行修改操作,而另一个客户端进行读取操作就阻塞了,直到锁被释放

image.png

总结:读锁不会阻塞其他客户端的读,但是会阻塞写。写锁既会阻塞其他客户端的读,又会阻塞其他客户端的写

2.元数据锁

元数据锁(meta data lock,MDL),MDL加锁过程是系统自动控制,无需显式使用,在访问一张表的时候会自动加上。MDL锁主要作用是维护元数据的数据一致性,在表上有活动事务的时候,不可以对元数据进行写入操作。 为了避免DML和DDL冲突,保证读写的正确性

image.png

这张表就是对应的sql所加上的元数据锁类型,其中读锁和写锁互相兼容,而exclusive(排他锁)与读写锁都互斥

lock in share mode是共享锁,后面会介绍

image.png

此处我们开启两个事务,这里进行的自动加上元数据锁都是共享锁(select语句和update语句),而共享锁之间是互相兼容的

再让我们看看下面这种情况

image.png

我们可以看到,另一个客户端阻塞了,原因是在当前客户端的事务中先执行了select语句,那么就会自动的加上读锁,而在另一个客户端进行update语句的时候(修改表结构)会自动加上exclusive这个排他锁,二者并不兼容,故阻塞

image.png

3.意向锁

image.png 比如在一个表中,线程A使用update语句条件为主键进行加行锁(索引知识),此时线程B并不知道又想去给这个表加上表锁,那么就会发生冲突,这个时候线程B要一行一行检查看看是否有行锁存在,效率十分低

为了避免DML在执行时,加的行锁与表锁的冲突,在InnoDB中引入了意向锁,使得表锁不用检查每行数据是否加锁,使用意向锁来减少表锁的检查

image.png 在加入行锁的同时给表加入一个意向锁,其他线程加表锁时只需要检查该表是否有意向锁即可(有意向锁会阻塞,等到线程A事务提交)

分类:

  • 意向共享锁(IS):由语句select...lock in share mode添加
  • 意向排他锁(IX):由语句insert、update、delete、select..for update添加

总结:意向锁是为了解决在InnoDB引擎中加的行锁和表锁的冲突问题

四.行级锁

行级锁,每次操作锁锁住对应的行数据。锁定粒度最小,发生锁冲突的概率最低,并发度最高。应用在InnoDB存储引擎中

InnoDB的数据是基于索引组织的,行锁是通过对索引上的索引项加锁来实现的,而表锁对记录加的锁

分类:

  • 行锁:锁定单个行记录的锁,防止其他事务对此行进行update和delete,在RC、RR隔离级别下都支持
  • 间隙锁:锁定索引记录间隙(不包含该记录),确保索引记录间隙不变,防止其他事务在中国间隙进行insert,产生幻读,在RR隔离级别下都支持
  • 临键锁:行锁和间隙锁组合,同时锁住数据,并锁住数据前面的间隙Gap,在RR隔离级别下支持

1.行锁

InnoDB实现了两种类型的行锁:

  • 共享锁(S):允许一个事务去读一行,阻止其他事务获得相同数据集的排他锁
  • 排他锁(X):允许排他锁的事务更新数据,阻止其他事务获得相同数据集的共享锁和排他锁

两种行锁的兼容情况如下:

image.png

2.间隙锁

指的是对一个索引范围中的"间隙"进行锁定,防止范围内间隙插入新数据,避免幻读(某个事务执行了一个范围查询(因为想插入一个新数据所以看看是否存在这个数据),发现想插入的数据并不存在,接着另一个事务在此范围内插入第一个事务想插入的数据,此时之前的事务执行插入操作发现这个数据又存在了,这就是幻读)

image.png

3.临键锁

临键锁与间隙锁的不同之处就在于,他锁定的不止是一个范围,还包括锁定的记录本身

假设,表中有一个范围id为(3,5]的next-key lock,那么其他事务既不能插入id=4的记录,也不能修改id=5的记录

image.png