MySql事务隔离的特点与实现
Posted 半生瓜丶
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了MySql事务隔离的特点与实现相关的知识,希望对你有一定的参考价值。
事务隔离的级别
mysql的事务隔离级别一共有四种情况。分别是:读未提交、读已提交、可重复读、串行化
- 读未提交:指的是一个事务可以读取到另一个未提交事务所做出的修改。
- 读已提交:指一个事务执行过程中可以读取另一个事务已提交的修改。
- 可重复读:指一个事务在启动的瞬间,事务所看到的数据保持不变。即使其它事务对数据有提交修改,对于可重复读隔离级别的事务也是不可见的。
- 串行化:指事务所读写的数据都需要通过悲观锁控制。采用读写锁的实现,当两个事务出现写冲突时,未获取锁的事务必须等待。
基于mvcc的事务隔离
mvcc全称为多版本并发控制(Multiversion concurrency control),应用于读提交和可重复读这两个隔离级别。所以下面讲解mvcc的实现就基于数据库是使用Innodb引擎的可重复读级别。
这个词语可以拆分为两个关键字,一个是多版本、一个是并发控制。那么通过引出两个问题,就可以初步的了解mvcc的控制下,用户事务能看到什么数据,怎么更新数据。
- 如何保存多个版本?
- 并发查询修改的时候,事务能看到哪个版本?
如何保存多个版本
首先,mysql中保存的数据,是只有一份的。但是通过undolog(回滚日志)的记录,能从当前数据计算出历史版本的数据。
那什么是undolog呢?undolog就是当某一行的数据发生变更时,数据库总会记录一份与当前操作的反向逻辑操作。如当前事务的操作把age=18+1,那么在事务提交的时候,就会在undolog记录这一行的回滚日志,大致是把age从19变为18。每个版本的记录都能被引擎轻易的找到。
但是仔细想想,undolog只记录了操作,那根据隔离级别的定义,哪个版本是当前事务可以读取的呢?所以undolog里面还记录了修改这条记录的事务id,而名称为:row trx_id。同时,事务id是严格递增的,不会出现重复。
并发查询修改的时候,事务能看到哪个版本
要解答这个问题,我们先来重复一遍可重复读得定义:一个事务在启动的瞬间,即使其它事务对数据有提交修改,事务所看到的数据保持不变。
这里的效果仿佛就是数据库引擎给我们创建的一个视图,后续的查询都在这个视图上进行,不受其它事务的影响。但是实际上,如果每个事务都创建一个视图,一个表上有几百m的数据,那事务一多起来数据库是必定崩溃的。
所以,这个的视图是逻辑上的视图,利用上面提到的undolog做多版本控制。在开启一个事务的时候,当前事务需要记录一次所有未提交的事务,并将它们用数组存储起来,同时确定最大值=max(事务id)+1与最小值=min(事务id)。
最小值的含义:小于这个值的事务是已经提交的。
最大值的含义:大于这个值的事务对于当前事务都是未开始的。
然后,当前事务查询一条数据时,当前的事务id会与这条记录的row trx_id作比较,如果这个row trx_id大于最小值怎么办?还记得之前的undolog吗,那就利用undolog做计算,计算出上一个版本的值,再与上个版本的row trx_id做比较,如果小于最小值,那就是可以看见的版本。
这样,每个事务都可以利用自己的事务id确定自己可以看见的数据,这就是逻辑上的视图。
事务隔离下的更新逻辑
从一个例子演示,可重复读事务隔离级别下是如何更新的。
有一个表:
id | k |
---|---|
1 | 1 |
下图中,有一个数据,最新的记录是由事务90提交的。按照时间线有如下操作:
1.然后事务B先开启,收集了当前未提交的事务id,形成逻辑视图。
2.事务C后开启,随后对k进行+1操作,当前版本k变为2,事务C提交。
3.事务B对k进行+1操作。
4.查询k的值。
问题:当前k的值是多少?
如果按照可重复读的隔离级别定义,这里事务B读到的k值应该为1才对。但是,这个理解是错误的,事务b查询到的k值应该为3。
-
为什么会这样?
那就要引入当前读这个概念:可重复读隔离级别下,事务开启后的数据读到的值不变,但是事务更改数据的时候,是读取最新版本的数据用作更新的。所以,当事务B执行k+1操作的时候,会读取最新版本k=2
然后再+1。 -
为什么需要当前读?
我认为,如果事务B在更新的时候不读取最新版本的值,而是使用自身建立的视图,那么就会把已提交的事务C所做出的修改给覆盖,这样也就违反了事务的持久性原则。 -
如果两个事务同时写怎么办?
当两个事务都对同一行进行修改时,那就需要用到写锁。先获取锁的事务可以进行修改,直到事务提交,另一个事务才能进行修改。
以上是关于MySql事务隔离的特点与实现的主要内容,如果未能解决你的问题,请参考以下文章