事务的隔离性与MySql隔离级别

1 事务隔离性

事务的基本要素(ACID)有四个,分别是原子性,一致性,隔离性和持久性。

原子性和持久性很好理解,一致性是指事务执行过程中的状态对外是不可见的,要么是执行前的状态,要么是执行后的状态,执行前是一个一致的状态,执行后是另外一个一致性状态。比如银行卡a向银行卡b转账,转账这个事务中有两个操作:将钱从卡a转出,将钱转到卡b。转账事务执行之前状态是一致的,转账事务执行了第一个操作之后,状态是不一致的,此时a卡上的钱少了,但b卡上的钱没有增加,状态是不一致的。执行完第二个操作,即将钱转到b卡上后,才具备正确的逻辑意义,此时的状态又是一致的了。

事务的隔离性指并发事务之间互相影响的程度,实质上并发事务互不影响才是事务隔离性的要求。但是为了提高并发,不得不做出一些妥协,所以又将事务的隔离性从低到高分为四个级别。

1.1 Read Uncommitted

读未提交,最低的隔离级别,在此级别上一个事务可以读到另一个事务未提交的结果。因此会出现脏读问题,读到的数据是一个临时的,不正确的数据。

1.2 Read Committed

读已提交,相对于读未提交来说的,指只有当事务提交之后,其更新结果才可以被其他事务看到。可以解决脏读问题,但是会存在另外的问题:不可重复读。例如事务a在事务b多次读取过程中提交了更新,此时事务b读到的结果是不一致的。

1.3 Repeated Read

可重复读,指在一个事务中,对于同一份数据的读取结果总是相同的,无论是否有其他事务对这份数据进行操作。可以解决脏读和不可重复读问题,但此隔离级别存在幻读问题。例如事务A新增了一条记录,事务B在事务A提交前后各执行了一次查询操作,发现后一次比前一次多了一条记录。不可重复读侧重于记录的修改和删除导致的不一致,而幻读侧重于记录的新增导致的不一致。

1.4 Serialization

事务串行化执行,最高的隔离级别,无并发。

2 MYSQL数据库隔离级别实现

MySql数据库的InnoDB存储引擎是支持事务的引擎,其默认的隔离级别为Repeated Read(简称RR)。ANSI SQL标准下RR事务隔离级别是Degree 2.9999的隔离性,但InnoDB引擎在RR的隔离级别下就解决了幻读问题,从而实现了Degree 3的隔离要求,真正满足了事务的隔离性要求。当然从严格意义上来说,InnoDB的RR事务隔离级别的实现与传统的Serialization级别还是有些不一样,比如唯一索引列在一个事务中允许重复值存在,不过这并不会破坏事务的一致性。这与InnoDB的锁和MVCC的实现有关。

另外值得一提的是SnapShot事务隔离级别,这是一种新的事务隔离级别,相比Serialization性能提升很多,Oracle、PostagreSQL、MS SQL Server等数据库已经开始支持了。但其仍然不符合真正隔离性的要求,存在两个问题:一是会导致“错误”的回滚;二是对于大事务的支持需要额外的内存保证,如果修改的数据量特别大,可能会导致内存溢出的问题发生。

2.1 可重复读隔离级别的底层实现原理

可重复读指一个事务多次读取数据时,不会读取到其他事务更新并提交的数据。如果用悲观锁来实现这一目标的话是很简单的,只需要在事务开始执行的时候对要读取的数据加上排它锁,事务执行完成后再释放锁就能保证整个过程中其他事务不能对加锁数据进行修改。但这样会严重影响性能。

在常用的数据库中,采用的是以乐观锁为理论基础的MVCC(多版本号并发控制)来解决这一问题。每条记录都有一个版本号,当更新记录时则将版本号加1。MVCC的实现没有固定的规范,每一个数据库都有不同的实现方式,下面将简单地介绍一下InnoDB的MVCC。

MVCC在MySQL的InnoDB中的实现

在InnoDB中,会在每行数据后加入两个额外的隐藏值来实现MVCC,这两个值一个记录这行数据核实被创建,一个记录这行数据何时过期(或者被删除)。在实际操作中,存储的并非时间,而是事务的版本,每开启一个新事务,事务的版本就会递增。在可重复读隔离级别下,遵循以下规则:

(1) SELECT时,读取“创建版本<=当前事务版本,删除版本为空或>当前事务版本”的数据记录。这样就保证了在当前事务之后的数据更新或删除不会被读取到。

(2) INSERT时,保存当前事务版本为行的创建版本。

(3) DELETE时,保存当前事务版本为行的删除版本。

(4) UPDATE时,插入一条新纪录。保存当前事务版本为行创建版本,同时保存当前事务版本到原来删除的行。

通过MVCC,尽管每行记录都需要额外的存储空间,但很其他的行检查工作以及一些额外的维护工作,但能够减低锁的使用,大多数读操作都不用加锁。

MySql中幻读问题的解决

正常情况下,RR隔离级别可以解决不能重复读问题,但任然会面临着幻读问题。MySql通过内部机制避免幻读,在rr隔离级别下几乎达到了串行化隔离级别的效果。

MySql中的读操作分为两种,一种是普通的select,采用的是MVCC来读的,无需加锁,这种读操作也称为快照读(snapshot read)。第一次读取时存储一个快照,后面的读取从快照获取结果即可,读到的数据是历史数据。

除此之外,还有一种读取操作需要加锁,如select … for update, select … lock in share mode, insert, update, delete等操作,这些语句在读取数据时就不通过mvcc来做快照读,而是读取实时的数据,这种读称之为当前读(current read)

对于快照读,MVCC可以防止幻读,因为读的是快照,每次结果都是一样。而对于当前读,则是通过记录锁和间隙锁来防止幻读。

总结来说,MySql在rr隔离级别下提供了两种事务隔离技术,一种是MVCC,另一种即Next-Key。普通的快照度无需加锁,使用MVCC;需要加锁的读通过Next-Key来实现。MVCC优点是无需加锁,并发性高,缺点是不是实时数据,而Next-Key则相反。

3 参考资料

(1) https://www.cnblogs.com/ws-astrologer/p/6681089.html
(2) https://blog.csdn.net/huanghanqian/article/details/79517480
(3) https://www.cnblogs.com/zsychanpin/p/7395635.html

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值