在 MySQL InnoDB存储引擎下,RC、RR 基于 MVCC 进行并发事务控制, MVCC 是基于“数据版本”对并发事务进行访问
用一个例子来解释一下,下面是一张事务执行流程图:
稍微解释一下:
事务 A
将id=1088
的记录的name
改为 “张三”,并提交事务 B
将id=1088
的记录的name
改为 “张小三”,并提交事务 C
将id=1088
的记录的name
改为 “张老三”,并提交事务 D
只是去查询
id=1088 的记录
首先模拟一下在 RC 隔离级别下:事务D 第一次读取的数据是 “张三”,第二次读取的就是“张小三”
这里应该很好理解,因为在 RC 隔离级别下,事务D 能读到 commit 的数据,所以就导致了“不可重复读”问题
如果使用 RR 隔离级别,事务D 的两次读取都是 “张三”
RR 隔离级别
是基于 MVCC
实现的,MVCC
又依赖 UNDO_LOG
,UNDO_LOG
就是回滚日志,它是一个版本链,它记录了上一次版本的数据变化
eg:事务 B 把“张三”改为了“张小三”,在UNDO_LOG
中的体现如下:
注:最原始的数据,也就是最下面这一条,它的 事务ID
和 回滚指针
都是 null
了解了 UNDO_LOG 之后,现在引入一个新的名词:ReadView
(快照读)
快照都就是普通的 sql 查询语句:select ....
有了快照读就有当前读,当前读是指:
Insert、Update、Delete、
Select ... for update
Select ... lock in share mode
图是从 IT老齐 那儿盗的,稍微解释一下:
m_ids 未提交的事务,不允许读
min_trx_id 就是快要执行完的事务 id
max_trx_id 就是下一个新的事务的 id
creator_trx_id 就是创建这个 ReadView 是哪个事务
RC(读已提交):在每一次执行快照读时生成 ReadView,根据上面的例子
读了两次,生成了两个 ReadView,先看第一个 ReadView,因为在这个 ReadView 生成的时候,事务1 已经 commit 了,所以他不在活跃事务集合里面,所以 m_ids 是 2,3,4
最小活跃事务就是 2 没有异议
预分配事务id 就是 4+1 = 5
当前的 ReadView 是事务 4 创建的,所以 creator_trx_id = 4
第二个 ReadView 也是一样的的逻辑
可重复读原理很简单,就是复用 ReadView
eg:在第二次查询的时候,生成的 ReadView 是复用前一次的,所以不会造成 不可重复读
能,但不完全能。
如果是多次快照读,ReadView会产生复用,没有幻读产生
但是当多次快照读中间存在当前读,ReadView会重新生成,导致产生幻读