MySQL 事务隔离级别与MVCC

一、隔离级别

参考了维基百科:事務隔離

隔离级别规定了一个事务中所做的修改,哪些在事务内和事务间是可见的,哪些是不可见的。

下面对隔离级别的说明都是基于锁机制并发控制的数据库管理系统而言。

1. 可序列化(Serializable)

实现可序列化要求在选定对象上的读锁和写锁保持直到事务结束后才能释放。在 SELECT 的查询中使用一个 WHERE 子句来描述一个范围时应该获得一个“范围锁(range-locks)”。这种机制可以避免“幻影读(phantom reads)”现象。

可序列化是最高级别的隔离。

2. 可重复读(Repeatable read)

该级别保证了同一个事务中多次读取同样的记录的结果是一致的。

对选定对象的读锁(read locks)和写锁(write locks)一直保持到事务结束,但不要求“范围锁(range-locks)”,因此可能会发生“幻影读(phantom reads)”。

幻影读:是因为没有保持范围锁,该事务执行了一个 where 子句的范围查询后,其他事务可能新增了一条处于该事务 where 查询范围内的记录,那么该事务再次执行范围查询时就会看到这些新增的记录行(幻行,Phantom row)。

可重复读是 MySQL 的默认事务隔离级别。

3.提交读(Read committed)

该级别也叫不可重复读(nonrepeatable read)。

DBMS需要对选定对象的写锁(write locks)一直保持到事务结束,但是读锁(read locks)在SELECT操作完成后马上释放(因此“不可重复读”现象可能会发生,见下面描述)。和前一种隔离级别一样,也不要求“范围锁(range-locks)”。

不可重复读是因为,事务只维持了选定对象的写锁,如果一些选定对象只涉及读锁,那么在读锁释放之后,其它事务可以对这些对象进行修改,该事务再次读取时就不一致了。

大多数数据库的默认事务隔离级别都是这个。

4. 未提交读(Read uncommitted)

也称为脏读(dirty read)。

一个事务可以读取到其它事务未提交的更改。

不可重复读的重点是修改:同样的条件,读取过的数据,再次读取出来发现值不一样了。

幻读的重点在于新增或者删除:同样的条件,第 1 次和第 2 次读出来的记录数不一样。

二、多版本并发控制

多版本并发控制(MVCC)的实现是通过保存数据在某个时间点的快照来实现的。根据事务开始的时间不同,每个事务对同一张表,同一时刻看到的数据可能是不一样的。

InnoDB 的 MVCC 是通过在每行记录后面保存两个隐藏的列来实现的:一个列保存了行的创建时间,一个保存行的过期时间(或删除时间)。存储的实际值是系统版本号(system version number)。

每开始一个新的事务,系统版本号都会递增。事务开始时刻的系统版本号作为事务的版本号,用来和查询到的每行记录的版本号进行比较。

在 REPEATABLE READ 隔离级别下,MVCC 的具体操作:

  • select:InnoDB 根据以下两个条件检查每行记录:

    a、 InnoDB 只查找版本早于当前事务版本的数据行(也即是行的版本号小于等于事务的系统版本号),这样可以确保事务读取的行,要么是在事务开始前已经存在的,要么是事务自身插入或者修改的。
    b、行的删除版本要么未定义,要么大于当前事务版本号。这可以确保事务读取到的行,在事务开始之前未被删除。
    只有符合以上两个条件的记录,才能返回作为查询结果。

  • insert:InnoDB 为新插入的每一行保存当前系统版本号作为行版本号。

  • delete:InnoDB 为删除的每一行保存当前系统版本号作为行删除标识。

  • update:InnoDB 为插入一行新纪录,保存当前系统版本号作为行版本号,同时保存当前系统版本号到原来的行作为行删除标识。


欢迎关注我的微信公众号: coderbee笔记,可以更及时回复你的讨论。

发表回复

您的电子邮箱地址不会被公开。 必填项已用*标注

此站点使用Akismet来减少垃圾评论。了解我们如何处理您的评论数据