事务的隔离级别

在定义中有四种隔离级别,每一种级别都规定了一个事务中所做的修改,哪些在事务内和事务间是可见的,哪些是不可见的。通常来说, 一个事务在最终提交前,对其它事务是不可见的。较低级别的隔离通常可以执行更高的并发,系统的开销也更低。

四种隔离级别

读未提交 READ UNCOMMITTED

在此级别中,即使事务中的修改没有修改,对其它事务也是可见的,事务可以读取未提交的数据,这就叫“脏读”。如果事务读取到其它未提交事务的数据,而刚好
该事务又回滚了,那么读取到的就是脏数据了

读提交 READ COMMITTED

读提交,顾名思义一个事务开始时,只能看见已经提交的事务所做的修改。大多数数据库默认的隔离级别就是它。这个级别有时候也叫 不可重复读
因为一个事务内,执行两次相同的查询,可能会得到不一样的结果。因为在两次查询中间可能有其它事务修改了其中的数据。

可重复读 REPEATABLE READ

该级别保证在同一事务内多次查询结果的一致的。但还是无法解决另外一个幻读的问题,指两次查询中间有其它事务插入数据,会产生 幻行
InnoDB和XtraDB存储引擎通过MVCC(多版本并发控制)解决了幻读的问题。 Mysql的默认隔离级别

可串行化 SERIALIZABLE

强制事务串行化执行,避免了幻读的问题。它会在读取的每一行数据上加锁,所以可能造成大量超时和锁争用问题。

隔离级别表

| 隔离级别 | 脏读 | 不可重复读 | 幻读 | 加锁读 |
| —- | —- |
| READ UNCOMMITTED | Yes | Yes | Yes | No |
| READ COMMITTED | No | Yes | Yes | No |
| REPEATABLE READ | No | No | Yes | No |
| SERIALIZABLE | No | No | No | Yes |

MVCC 多版本并发控制

大多数的数据库事务存储引擎为了提升并发性能,都会实现各自的多版本并发控制。MVCC可以看成是行级锁的表中,但在大多数情况下避免了加锁的操作,
因此性能开销更低。MVCC基本都实现了非阻塞的读操作和只锁定目标行的写操作。

MVCC的实现是通过某个时间点的快照来实现的。也就是说,根据事务开始的时间点不同,不同的事务在同一张表,同一时刻读取的的数据有可能是不用的。
不同的MVCC有自己的不同实现,典型的有乐观并发控制和悲观并发控制。

InnoDB的MVCC是通过在每一行记录后增加两个隐藏的列来实现的。一列保存行创建的事务版本号,一个保存行删除的事务版本号,每开始一个事务,事务版本号都会自动递增。
在事务开始时刻的版本号会作为此次事务的版本号,用来和查询到的每行记录的版本号作比较。

下面为在Mysql默认隔离级别REPEATABLE READ下的MVVC是如何控制的:

  • SELECT

    • InnoDB只查找版本早于当前事务的数据行(版本号小于或等于事务的版本号),这样可以保证查询到的数据要么是之前已经存在,要么是当前事务自己插入或修改过的的
    • 行的删除版本号要么未定义,要么版本号大于当前版本号

只有符合以上两个条件的记录,才会作为结果返回。

  • INSERT

InnoDB为新插入的每一行保存当前的版本号为行创建版本号

  • DELETE

InnoDB为删除的每一行保存当前的版本号为行删除版本号

  • UPDATE

InnoDB为插入一行新纪录,保存当前的版本号为行的创建版本号,同时保存当前版本号到原来的行的删除版本号


保存这两个版本号使得大部分时候都可以不用加锁。这样设计性能很好,并且保证只会读取到符合结果的行。不足时会额外增加存储空间和做一些额外的维护工作。

MVCC只在READ COMMITTED和REPEATABLE READ两个隔离级别下工作,因为READ UNCOMMITTED总是读取最新的数据行,而不是符合当前事务版本的,
SERIALIZABLE则会对读取的行加锁。

评论