Mysql 之InnoDB存储引擎中的锁

锁的分类

InnoDB存储引擎实现了如下两种标准的行级锁

S Lock(共享锁):允许事务读一行数据。

X Lock(排他锁):允许事务删除或者更新一行数据。

注意,InnoDB同样也支持表级别的S和X锁,不要误认为S和X锁就只是针对行的。

如果一个事务T1已经获得了行r的共享锁,那么另外的事务T2可以立即获得行r的共享锁,因为读取并没有改变行r的数据,称为这种情况为锁兼容。但是若有其他事务T3想获得行r的排他锁,则其必须等待事务T1、T2释放行r上的共享锁——这种情况称为锁兼容。共享锁默认是并存的,但是共享锁和排它锁不共存。
在这里插入图片描述
InnoDB存储引擎支持多粒度的锁定,这种锁允许事务在行级上的锁和表级上的锁同时存在。此外,InnoDB还支持一种额外的锁方式,称之为意向锁。意向锁是将锁定的对象分为多个层次,意向锁意味着事务希望在更细的粒度上加锁。

意向锁的级别是表级别,分为两种:

IS Lock(意向共享锁),事务想要获得一张表中某几行的共享锁。

IX Lock(意向排他锁),事务想要获得一张表中某几行的排他锁。

意向锁的加锁顺序为:要加细粒度的锁,必须先加粗粒度的锁。
在这里插入图片描述
这个表格要好好理解一下,否则容易出现偏差,就是这个表格里面的IS/IX/S/X在此处都是针对的表级别在讨论,不要认为此处的S和X是说的行级别,受这个影响的人会对加锁的过程感到疑惑的,可能大家都是受了翻译书的影响,因为,书中在介绍这一个图是是这么说的,原文如下,注意文中说的故表级意向锁和行级锁的兼容性如下,这是错误的说法。

IX,IS是表级锁,不会和行级别的X,S冲突。只会和表级别的X,S冲突。比如说alter操作,drop操作,lock操作是不是对于书里那句话很讽刺啊,说实话我看的时候也是一脸懵逼,这什么时候出现了这个概念了?比较的竟然是表级别和行级别的。

意向锁的作用

数据库表中的共享锁和意向锁可以同时共存,当两者共存的时候就会出现一些问题。考虑下面这个情况:

事务A锁住了表中的一行,让这行只能读,不能写。之后事务B申请整个表的写锁,如果事务B获取锁成功,那么理论上事务B可以对表做任何修改,这与事务A相冲突。数据库需要避免这种冲突,就是说要让B的申请被阻塞,直到A释放了行锁。

数据库要怎么判断这个冲突呢?

step1:判断表是否已被其他事务用表锁锁表
step2:判断表中的每一行是否已被行锁锁住。

注意step2,这样的判断方法效率实在不高,因为需要遍历整个表。
于是就有了意向锁。

在意向锁存在的情况下,事务A必须先申请表的意向共享锁,成功后再申请一行的行锁。

在意向锁存在的情况下,上面的判断可以改成

step1:不变
step2:发现表上有意向共享锁,说明表中有些行被共享行锁锁住了,因此,事务B申请表的写锁会被阻塞。

注意:申请意向锁的动作是数据库完成的,就是说,事务A申请一行的行锁的时候,数据库会自动先开始申请表的意向锁,不需要我们程序员使用代码来申请。

mysql数据库的四大特性及事务的隔离级别

事务的基本要素(ACID)

原子性:事务开始后的操作要么全部完成,要么全部不完成,不存在只完成一部分的情况。就想化学里面的原子一样,不可以再分割。
一致性:事务开始前和开始后,数据库的完整性约束没有被破坏。比如A向B转了钱,不可能A的钱减少了,B的钱没增加。
隔离性:同一时间只允许一个事务对同一数据操作,事务之间比应该相互干扰,比如A正在从一张银行卡中取钱,在A取钱的过程结束前,B不能向这张卡转账。
持久性:事务完成后,事务对数据库的所有操作都应该保存在数据库里面,不能够回滚。

事务的并发问题

脏读:事务A 读取了事务B更新的数据,但是这个时候事务B却把数据回滚了,这个时候A读到的数据就是脏数据(注意是两个事务对同一数据)。
不可重复读:事务A多次读取同一数据(注意事务A 没有结束),在读取的过程中,事务B对数据进行了更新并提交,这样事务A 在读取的过程中就会出现读取的结果前后不一样的情况。(注意这和事务的隔离性没有冲突,因为隔离要求不同事物在同一时间不可以对同一数据进行操作,强调同一时间)。
幻读:系统管理员A将学生成绩表对应的分数全部转换成ABCD四个等级,此时系统管理员B插入了一条新的学生数据,成绩为分数。此时管理员A发现还有一条记录没有改过来,就好像发生了幻觉一样,这就叫幻读。

小结:不可重复读的和幻读很容易混淆,不可重复读侧重于修改,幻读侧重于新增或删除。解决不可重复读的问题只需锁住满足条件的行,解决幻读需要锁表。

mysql事务的隔离级别

在这里插入图片描述
下面我们来一一验证(参考https://www.cnblogs.com/shihaiming/p/11044740.html):

mysql默认的事务隔离级别为repeatable-read(可重复读)。

1、读未提交:

(1)打开一个客户端A,并设置当前事务模式为read uncommitted(未提交读),查询表account的初始值:

set session transaction isolation level read committed

在这里插入图片描述
在客户端A的事务提交之前,打开另一个客户端B,更新表account:
在这里插入图片描述
这时,虽然客户端B的事务还没提交,但是客户端A就可以查询到B已经更新的数据(为什么可以读到呢,因为最先设置了数据库的隔离级别为读未提交):
在这里插入图片描述
一旦客户端B的事务因为某种原因回滚,所有的操作都将会被撤销,那客户端A查询到的数据其实就是脏数据:
在这里插入图片描述
在客户端A执行更新语句update account set balance = balance - 50 where id =1,lilei的balance没有变成350,居然是400,是不是很奇怪,数据不一致啊,如果你这么想就太天真 了,在应用程序中,我们会用400-50=350,并不知道其他会话回滚了,要想解决这个问题可以采用读已提交的隔离级别。
在这里插入图片描述
2、读已提交

(1)打开一个客户端A,并设置当前事务模式为read committed(未提交读),查询表account的所有记录:
在这里插入图片描述
在客户端A的事务提交之前,打开另一个客户端B,更新表account:
在这里插入图片描述
这时,客户端B的事务还没提交,客户端A不能查询到B已经更新的数据,解决了脏读问题:
在这里插入图片描述
客户端B的事务提交
在这里插入图片描述
客户端A执行与上一步相同的查询,结果 与上一步不一致,即产生了不可重复读的问题。
在这里插入图片描述
3、可重复读

打开一个客户端A,并设置当前事务模式为repeatable read,查询表account的所有记录。
在这里插入图片描述
在客户端A的事务提交之前,打开另一个客户端B,更新表account并提交
在这里插入图片描述
在客户端A查询表account的所有记录,与步骤(1)查询结果一致,没有出现不可重复读的问题
在这里插入图片描述
在客户端A,接着执行update balance = balance - 50 where id = 1,balance没有变成400-50=350,lilei的balance值用的是步骤(2)中的350来算的,所以是300,数据的一致性倒是没有被破坏。可重复读的隔离级别下使用了MVCC机制,select操作不会更新版本号,是快照读(历史版本);insert、update和delete会更新版本号,是当前读(当前版本)。
在这里插入图片描述
重新打开客户端B,插入一条新数据后提交
在这里插入图片描述
在客户端A查询表account的所有记录,没有 查出 新增数据,所以没有出现幻读

在这里插入图片描述4.串行化

打开一个客户端A,并设置当前事务模式为serializable,查询表account的初始值:
在这里插入图片描述
打开一个客户端B,并设置当前事务模式为serializable,插入一条记录报错,表被锁了插入失败,mysql中事务隔离级别为serializable时会锁表,因此不会出现幻读的情况,这种隔离级别并发性极低,开发中很少会用到。
在这里插入图片描述
补充:

1、事务隔离级别为读提交时,写数据只会锁住相应的行
2、事务隔离级别为可重复读时,如果检索条件有索引(包括主键索引)的时候,默认加锁方式是next-key 锁;如果检索条件没有索引,更新数据时会锁住整张表。一个间隙被事务加了锁,其他事务是不能在这个间隙插入记录的,这样可以防止幻读。
3、事务隔离级别为串行化时,读写数据都会锁住整张表
4、隔离级别越高,越能保证数据的完整性和一致性,但是对并发性能的影响也越大。
5、MYSQL MVCC实现机制参考链接:https://blog.csdn.net/whoamiyang/article/details/51901888
6、关于next-key 锁可以参考链接:https://blog.csdn.net/bigtree_3721/article/details/73731377

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值