InnoDB关于事务、锁、MVCC专题( 五 )


为什么要有undo日志?
undo log 指事务开始之前,在操作任何数据之前,首先将需操作的数据备份到一个地方 。
数据库事务未提交时,会将事务修改数据的镜像(即修改前的旧版本)存放到undo日志里,当事务回滚时,或者数据库奔溃时,可以利用undo日志,即旧版本数据,撤销未提交事务对数据库产生的影响 。
事务未提交之前,Undo 保存了未提交之前的版本数据,Undo 中的数据可作为数据旧版本快照供其他并发事务进行快照读 。是为了实现事务的原子性而出现的产物,在 MySQL innodb 存储引擎中用来实现多版本并发控制 。
当前读和快照读当前读(Locking Read)也称锁定读,读取当前数据的最新版本,而且读取到这个数据之后会对这个数据加锁,防止别的事务更改即通过next-key锁(行锁+gap锁)来解决当前读的问题 。在进行写操作的时候就需要进行“当前读”,读取数据记录的最新版本,包含以下SQL类型:select ... lock in share mode 、select ... for updateupdate 、delete 、insert
快照便是进行select的那一刻,生成的当前数据库系统中所有未提交的事务id数组(数组里最小的idmin_id)和已经创建的最大事务idmax_id)的集合,即我们所说的一致性视图readview 。在进行快照读的过程中要根据一定的规则将版本链中每个版本的事务idreadview进行匹配查询我们需要的结果 。快照读是不会看到别的事务插入的数据的 。因此,幻读在“当前读”下才会出现 。快照读的实现是基于多版本并发控制,即MVCC,可以认为MVCC是行锁的一个变种,但它在很多情况下,避免了加锁操作,降低了开销;既然是基于多版本,即快照读可能读到的并不一定是数据的最新版本,而有可能是之前的历史版本 。
RC和RR隔离级别下的快照读和当前读:RC隔离级别下,快照读和当前读结果一样,都是读取已提交的最新;RR隔离级别下,当前读结果是其他事务已经提交的最新结果,快照读是读当前事务之前读到的结果 。RR下创建快照读的时机决定了读到的版本 。
总结:快照读本质上读取的是历史数据(原理是回滚段),属于无锁查询

  1. Serializable 隔离级别下 - 普通 select 也变成当前读,即加共享读
  2. 在 RC 隔离级别下 - 每次 select 都会建立新的快照
  3. 在 RR 隔离级别下
    1. 事务启动后,首次 select 会建立快照
    2. 如果事务启动选择了 with consistent snapshot,事务启动时就建立快照
    3. 基于旧数据的修改操作,会重新建立快照
【InnoDB关于事务、锁、MVCC专题】

经验总结扩展阅读