事务隔离性的两种实现方式

Posted 毛奇志(公众号:爱奇志)

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了事务隔离性的两种实现方式相关的知识,希望对你有一定的参考价值。

一、前言

二、MVCC 多版本并发控制

事务的目的是要实现 读一致性 ,有两种方法:加锁(基于锁的并发控制 LBCC) 和 多版本并发控制MVCC,加锁就是四种隔离级别对应的锁。

在这里插入图片描述
MVCC的规范,如下:第一次查询就确定了快照版本,

能读取到,快照建立后创建的事务已提交的修改,不能读取到,快照建立后创建的事务

能读取到,当前自己事务的修改(包括未提交的),不能读取到,其他事务未提交的修改;

在这里插入图片描述

InnoDB为每个表提供了三个隐藏的字段,

在这里插入图片描述
通过一个图来理解事务id和删除版本号

navicat打开一个会话,Transaction 1 插入两条数据,
在这里插入图片描述
Transaction 2 表示第一次查询就确定了快照版本,
在这里插入图片描述

在这里插入图片描述

在这里插入图片描述
执行顺序为,
Transaction 1 插入两条数据
Transaction 2 查询
Transaction 3 新插入的第三条数据
Transaction 2 查询
Transaction 4 删除第二条数据
Transaction 2 查询
Transaction 5 修改第一条数据
Transaction 2 查询

Tranaction 2 第一次查询确定了快照版本之后,后面的无论 Transaction 3 新插入的第三条数据、 Transaction 4 删除第二条数据、Transaction 5 修改第一条数据,都不会影响查询结果,因为查找已经建立了,只能看到Transaction 1最初插入的两条数据。

MVCC底层原理是read view,一致性视图。每一个事务,第一次查询,会建立了

在这里插入图片描述
在这里插入图片描述
第一条,本事务,create view 第四个
第二条,前面是的事务,create view 第二个
第三条,后面的事务,create view 第三个
第四条,活跃(未提交)就不可以读取,create 第一个,

对于MVCC,不同的表类型生成快照的时机有不同,只有repeat read利用mvcc解决了幻读问题(update delete insert都无法影响到)
在这里插入图片描述
MVCC的局限:对于读一致性,只能保存第一次查询的视图,不能查询到最新的数据,只能 LBCC ,就是加锁。

三、LBCC 基于锁的并发控制

通过数据库资源的不同粒度的划分,来阐述隔离性不同级别的实现。隔离级别的底层实现方式就是:数据库锁的不同级别导致资源互斥粒度的不同,依次是:表级锁,行级锁,读写分离锁,不加锁。

3.1 从表锁到行锁

从表锁到行锁:MyISAM仅仅支持表锁,InnoDB支持表锁和行锁,行锁可以在保证数据读一致性的同时,带来更高的并发性能,这是InnoDB的进步。

表锁就是:
在这里插入图片描述

表锁和行锁的区别:
在这里插入图片描述
下面都是行级锁,因为只有行级锁才是InnoDB的精髓,在保证 读一致性 下实现了比较高的效率。

从锁到事务的关系:锁是读一致性的一种实现方案(另一种实现方案是MVCC),读一致性能够实现事务隔离,实现了事务隔离就提供了四种不同事务隔离级别,事务隔离级别是事务的一大特性。

InnoDB行锁的分类(8类):

基本锁:读共享锁、写独占锁、意向锁

锁的算法(在什么情况下锁定什么范围):记录锁、间隙锁、临键锁

不重要:插入意向锁、自增锁、

在这里插入图片描述
在这里插入图片描述

3.2 四种基本锁

3.2.1 读共享锁

在这里插入图片描述
共享锁:加锁之后就不允许别人修改,这样自己读的时候就可以读到一致性的了,但是自己也不能修改

不同事务可以在同一行都加上 读共享锁 ,反正大家(加了读锁的事务、美甲读锁的事务)都不能修改。

和其他行级锁相同,如果 navicat 会话连接断开,锁释放。
在这里插入图片描述

3.2.2 写独占锁

目的:自己加上了别人不能再加上读共享锁、也不能再加上写独占锁。别的事务执行select或者udpate/insert/delete,等待50s,无法成功,就会失败。

自己加上了写独占锁,当前事务既然可写,也可读。

什么时候加写独占锁?
1、写insert update delete,默认加上了写独占锁,只有当前事务可读可写(但是select默认不加上写锁,也默认不加上读锁)。
2、对于select,写成 select … for udpate,就加上了写独占锁。写成 select … lock in shared mode,就加上了读共享锁。

3.2.3 意向读共享锁(表锁) + 意向写独占锁(表锁)

问题:这两个表锁(意向共享锁、意向排他锁)存在的意义是什么?

一个事务给一行数据加上锁(共享锁、独占锁),如果另一个事务想来加锁,必须全表扫描,这样效率太慢。所以,mysql设计

如果一个事务给一行数据,加上一个读共享锁,那么先给这个表加上一个意向共享锁。
如果一个事务给一行数据,加上一个写独占锁,那么先给这个表加上一个意向独占锁。
这样设计的理由:一个事务可以给一张表加表锁的前提是:没有任何一个事务给这张表的任何一个记录加行锁。

这样一来,每个事务加行锁之前,只要看一下这个表上有没有意向共享锁和意向排他锁,只要有,自己就不加锁了,从来提高判断效率。

所以,这两个表锁(意向共享锁、意向排他锁)大大的提高了不同事务给表加行锁的效率,这就是这两个表锁存在的最大意义。

在这里插入图片描述

3.2.4 两个行锁锁住的实际是一行记录的索引

共享锁,一个事务给一行数据加锁,另一个事务还可以给这行数据加锁;
排他锁,一个事务给一行数据加锁,另一个事务不可以给这行数据加锁。
底层是怎么实现的?所谓的共享锁和排他锁锁住的是一行数据的什么东西呢?实际上,锁住的是索引,普通索引也行,唯一索引也行、主键索引也行。

在INNODB_LOCKS
在这里插入图片描述

在一张表中,如果没有设置索引,就会有InnoDB中隐藏列rowid作为聚集索引,此时,会锁住整个表。

对于行锁,
如果存在索引,锁住的索引,如果where条件中有索引列,仅锁住where条件命中的这一行或多行数据;如果where条件中没有索引列 或者 没有根本就没有where子句,会锁住整个表(连插入都插入不进去了)。
如果不存在索引,锁住的隐藏的rowid,此时会锁住整个表(连插入都插入不进去了)。

3.3 三种高级锁

什么情况下锁住什么范围?先理解记录、间隙、临键三个概念
在这里插入图片描述

对于索引列,无论是整数还是字符,都可以排序。

3.3.1 记录锁

对于唯一性索引(唯一索引、主键索引),如果where条件命中数据行,
仅仅锁住单个记录,此时使用记录锁,包括行读共享锁、行写排他锁。
在这里插入图片描述

3.3.2 间隙锁

如果where条件没有命中数据行,即where条件在数据库中找不到数据,

间隙锁和间隙锁本身不冲突,因为间隙锁的设计是专门用来阻塞插入的,间隙锁只有在InnoDB的可重复读隔离级别中才会有间隙锁,在Innodb的未提交读、已提交读没有间隙锁,这就是为什么使用可重复可以避免幻读错误。

先用where id>4 and id<7加锁,第二个事务还可以用where id=6加锁,不冲突,但是如果insert id=5,会失败。

如果用where id >20 会锁住 (10,正无穷大) ,如果insert id = 11 ,会失败。

在这里插入图片描述

3.3.3 临键锁

临键锁和间隙锁唯二区别是锁住的空间是右边闭区间,还有就是因为临键锁是间隙锁和记录锁的合并。所以,临键锁和临键锁是冲突的,如果第二个事务加锁到记录锁上面,不能成功,for update 写锁是独占的,如果第二个事务加锁…

如果在第一个事务
在这里插入图片描述

3.4 四种隔离级别,哪种mvcc,哪种加锁

mvcc只有在RC RR中有,

在read uncommited啥都没有,没有锁,没有mvcc,
在rc里面,有mvcc,有锁,只有记录锁,没有间隙锁,所以无法解决幻读问题
在rr里面,有mvcc,有锁,
在serializable,只有锁,没有mvcc
在这里插入图片描述

在这里插入图片描述

四、尾声

事务隔离性的两种实现方式,完成了。

天天打码,天天进步!!

以上是关于事务隔离性的两种实现方式的主要内容,如果未能解决你的问题,请参考以下文章

spring 中常用的两种事务配置方式以及事务的传播性隔离级别

MySQL数据库事务隔离性的实现

spring 中常用的两种事务配置方式以及事务的传播性隔离级别

MySQL数据库事务隔离性的实现

MySQL数据库事务隔离性的实现

mysql的两阶段协议(封锁定理,虫洞事务)