事务隔离性的两种实现方式
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 中常用的两种事务配置方式以及事务的传播性隔离级别