在上一篇文章中详细介绍了MVCC机制,它解决的是在有其他的事务在更新时如何读的问题,而今天将要介绍的解决的是其他事物在更新时如何更新的问题。其实加锁这一机制不仅仅是在MySQL中,在很多计算机语言中你都能看到,就比如Java,还有中间件,例如redis也有锁机制。总而言之,如果没有锁机制,我们计算机世界将会混乱不堪,程序之间无法协作,代码也将极其复杂。
在MySQL中,锁控制着对资源(如表、行或内部数据结构)的访问,比如不能同时有1个以上的事务去更新同一行数据或者同一个表格,又或者不允许一个事物更新表格的时候另一个事物去插入或者删除表中的某些数据。
InnoDB中的锁使用的是一种轻量级结构,事务通常会持有一段短时间(以毫秒或微秒计)。这个应该也很好理解,A和B两个人事物同时更新 同一行数据,A先加上锁,然后就开始更新数据了,此时B也想更新数据,那么他会去看这个锁是否有人持有,如果有,就等待,过会儿再看看是否有人持有。在上述过程中每个事物都会生成一个锁的数据接口,里面记录了trx_id和等待状态,事物去更新一行数据的时候会将这个锁加在那行数据上,其他事物如果说发现已经被抢先一步就回将自己的锁的等到状态设置为true,先进行等待,然后过会儿再试。就像下面这样
上面这个例子中的锁是一个互斥锁,也就是说,最多只能一个人给数据加锁。在数据库中叫排它锁
在InnoDB中一共有如下种类的锁:
下面我们来看看他们分别是什么,这里只介绍和我们最息息相关的,对于一些很少见的大家可以自行查阅相关资料
共享锁/排它锁
英文名叫Shared and Exclusive Locks,所以也简称S锁和X锁,这个锁的功能其实顾名思义。下面进行举例:A线程给数据加锁S锁之后,B线程也可以给这个数据加S锁,但是却不能给这个数据加X锁,因为X锁是互斥的,同时只允许一个事物进行加锁。相信这个不难理解,那么应用场景呢?应该不难知道,S锁允许多个事务共享一行数据肯定是所有加S锁的事务在读的时候,这样不会发生混乱,那么X锁呢?其实也很简单肯定是在更新或者删除的时候,因为这种操作是绝对不允许多个事务对同一个数据同时进行的!
因此我们不难知道,S锁和X锁的的作用:
S锁:允许持有锁的事务进行读操作
X锁:允许尺有锁的事务进行更新或者删除操作
注:S锁和X锁都是加载行上的,因此也被称为行锁
意向锁
这个锁只有在理解了其含义后才明白“意向”的含义。InnoDB中的锁支持多粒度,上面说了行锁,这个意向锁其实是表锁,分为意向共享锁(IS锁)和意向排他锁(IX锁),是加锁在表上的。那这个意向锁是如何用的呢?回忆一下,我们是如何找到一条数据的,是不是先找到对应的表,然后才能找到对应的行对吧。这个加锁流程其实也是一样的,获取行上的锁之前需要先获取表上的锁,在给后一行加S锁时需要先获取表上的IS锁,同理,在给某一行加X锁时需要先获取表上的IX锁。(注:与S/X锁不一样的是,多个事务给表加IS锁和IX锁是允许的,但是一时间只能有一个事物可以获取到表上的IX锁)。
下面是加锁和获取锁的SQL语句:
# 可以通过下面的语句来获取指定表的意向排它锁(获取表的IX锁,只能有一个事物获取到) LOCK TABLES ... WRITE # 加上意向共享锁(加IS锁,同一个表可以加多个) SELECT ... LOCK IN SHARE MODE # 加上意向排他锁 SELECT ... FOR UPDATE(加IX锁,同一个表可以加多个) # 查看事物的加锁状态(在Status列中的TRANSACTIONS部分可以查看) SHOW ENGINE INNODB STATUS
下面是这些锁之间的兼容关系:
X | IX | S | IS | |
---|---|---|---|---|
X | 冲突 | 冲突 | 冲突 | 冲突 |
IX | 冲突 | 兼容的 | 冲突 | 兼容的 |
S | 冲突 | 冲突 | 兼容的 | 兼容的 |
IS | 冲突 | 兼容的 | 兼容的 | 兼容的 |
至此,我们不难得出意向锁的作用:
除了LOCK TABLES ... WRITE
这一全操作之外,意向锁不会阻止任何事,他仅仅只是表名意图,表示有事物正在或者之后可能会对某一行数据进行操作,这也是“意向”这一名字的由来
记录锁
记录锁,顾名思义,它是被用在索引的记录上的。当某个索引的某一个记录加上记录锁之后,可以防止其他事物来插入、更新或者删除这一条记录。前面所说的行级锁本质上就是记录锁
如果你没有建立索引,那么InnoDB会创建一个隐藏的局聚簇索引
间隙锁
间隙锁,顾名思义,是用来锁住间隙的,那是什么的间隙呢?其实就是索引的间隙,你可能会有疑问,什么情况下才需要锁住索引的间隙?我现在举一个例子:现在A事务在执行这样一条SQL语句:SELECT c1 FROM t WHERE c1 BETWEEN 10 and 20
,在A事务执行完这条SQL后来了一个B事务插入了数据c1 = 11,紧接着A事务在提交前又执行了一次这个语句,此时A事务发现前后两次从同一个范围内获取到的数据竟然不一样?这种情况就是之前文章中说的幻读
,就像是发生了幻觉一样!
对于这种WHERE条件中有范围条件的,为了防止发生幻读的情况,我们需要给这个范围内的间隙加上间隙锁,SQL的写法是这样的:SELECT c1 FROM t WHERE c1 BETWEEN 10 and 20 FOR UPDATE
,这样做之后会阻止其他事物往10到15这个范围内插入数据的操作
间隙可能跨越单个或多个索引值,甚至也可以是空的。如果使用唯一索引来搜索唯一行的语句是不需要间隙锁的。
SELECT * FROM child WHERE id = 100;
如果 id
列不是索引或者有非唯一索引,则该语句会锁定前面的间隙。
值得注意的是对于同一个间隙,可以允许多个事务对其加上间隙锁。
理解了上面的内容后,我们不难得出间隙锁的作用:
间隙锁具有”纯粹的一致性“,它的唯一目的就是防止其他事物插入间隙。间隙锁可以共存。一个事务采用的间隙锁不会阻止另一个事务在同一间隙上采用间隙锁。共享间隙锁和排他间隙锁之间没有区别。它们彼此不冲突,并且执行相同的功能。
Next-Key锁
从名字是确实不太好理解,我先说一下这个锁是什么样的,所谓的Next-Key锁其实就是间隙锁+记录锁的组合,它的作用是用来防止幻读的发生,前面说了间隙锁,它只能保证一个事物中前后两次范围查询的数据量一样,但是却不能防止数据被修改,因此,单靠间隙锁可能出现不可重复读的情况,为了防止这种情况发生,加上看记录锁来配合使用。
InnoDB,默认是REPEATABLE READ隔离界别,在搜索或者扫描索引的时候会在遇到的间隙和记录上加上间隙锁和记录锁,也就是Next-Key锁,一个接着一个,这样可以防止其他事物在前面插入新记录。这可能就是Next-Key锁名字的由来吧,如同其加锁的行为——一个接着一个
综上可知Next-Key锁的作用:
InnoDB在REPEATABLE READ级别下使用Next-Key锁可以防止幻读的发生
其他锁
对于意向插入锁、AUTO-INC锁和谓词锁因为比较偏,本篇就不详细阐述了,有兴趣可以自行去查阅相关资料。在理解了上面讲解的锁之后,相信剩下的这几个锁肯定也不难理解
今天介绍了InnoDB中锁,以及常见的类型,他们的粒度、作用各不相同,是为了解决不同的问题而诞生的,因此理解他们是被用来解决什么问题至关重要!
以上就是本篇的全部内容了,如果有什么地方有问题,欢迎指出,共同进步