沫言|解读数据库:深入分析MySQL中事务以及MVCC的实现原理( 三 )


事务的隔离级别Read Uncommitted(未提交读)简称RU 。 这种是最低的隔离级别 , 等于没有隔离 , 基本上没有数据库会使用这个级别 。 一个事务可以读取到其他事务未提交的数据 , 这种也叫做脏读 。
什么是脏读?请看下面这个例子:
沫言|解读数据库:深入分析MySQL中事务以及MVCC的实现原理左边是事务1 , 先查一次 , 查到id为1的数据name为张三 , 这时候事务2又来了 , 把张三改成了李四 , 然后事务1又进行了一次查询 , 查出来了name为李四 , 那么假如这时候事务2发生了回滚 , 也就是name还是张三 , 但是事务1却读到了李四 , 这就是脏读 。
Read Committed(已提交读)简称RC 。 一个事务只能读取到其他事务已提交的数据 , 就是说在一个事务里面 , 执行同样的查询 , 会出现两次不一样的结果 。 Oracle和SQL Server数据库默认的数据库隔离级别 。 这种隔离级别解决了脏读问题 , 但是会出现不可重复读的问题 。
什么是不可重复读?还是看上面那个例子 , 假设事务2更新之后马上就提交 , 然后事务1第二次查询查出来的结果还是李四 , 只是这次就不算是脏读了 , 因为事务2提交了 , 这种就叫不可重复读 , 因为事务1中两次查询同一条数据结果不一样 。
Repeatable Read(可重复读)简称RR 。 这种隔离级别解决了不可重复读问题 , 就是说在同一个事务中 , 执行相同的查询 , 结果都是一样的 , 但是这种级别会出现幻读问题(InnoDB引擎例外 , InnoDB引擎通过间隙锁解决了幻读问题) 。
什么是幻读?请看下面这个例子:
沫言|解读数据库:深入分析MySQL中事务以及MVCC的实现原理上面图形中 , 事务1进行了一个范围查询 , 第一次只能查出一条记录 , 这时候事务2来插入了一条数据 , 然后事务1再次执行同一个查询 , 这时候就能查出来两条记录 , 也就是多了一条 , 给人一种幻觉 , 所以称之为幻读 。
说到这里 , 可能有人就有疑问了 , 因为感觉不可重复读和幻读都是读取到已提交事务的结果 , 好像没什么区别?确实如此 , 不可重复读和幻读本质上是一样的 , 但是不可重复读针对的是更新和删除操作 , 而幻读仅针对插入操作 。
Serializable(串行化)这种是隔离的最高级别 , 也就是说所有的事务都是串行执行的 , 也就不存在并发事务 , 脏读 , 可重复读和幻读问题自然也就没有了 。
不同隔离级别对比不同的隔离级别可以解决不同的问题 , 大致如下图:
沫言|解读数据库:深入分析MySQL中事务以及MVCC的实现原理对于未提交读和已提交读大家可能都很好理解 , 只要控制一个事务提交之后才能对另一个事务可见 , 但是对于可重复读 , MySQL到底是如何实现即使一个事务已经提交了 , 还能对另一个事务不可见呢?这就是接下来我们要讲解的MVCC了 。
事务隔离的实现方案事务隔离的实现方案有两种 , LBCC和MVCC
LBCCLBCC , 基于锁的并发控制 , 英文全称Based Concurrency Control 。 这种方案比较简单粗暴 , 就是一个事务去读取一条数据的时候 , 就上锁 , 不允许其他事务来操作(当然这个锁的实现也比较重要 , 如果我们只锁定当前一条数据依然无法解决幻读问题) 。
当前读这个概念其实很好理解 , MySQL加锁之后就是当前读 。 假如当前事务只是加共享锁 , 那么其他事务就不能有排他锁 , 也就是不能修改数据;而假如当前事务需要加排他锁 , 那么其他事务就不能持有任何锁 。 总而言之 , 能加锁成功 , 就确保了除了当前事务之外 , 其他事务不会对当前数据产生影响 , 所以自然而然的 , 当前事务读取到的数据就只能是最新的 , 而不会是快照数据(后文MVCC会解释快照读概念) 。