MVCC
Multi Version Concurrency Control的简称,代表多版本并发控制,实现非锁定一致性读
概念:
??非锁定读??快照读(查询的行执行删除或修改操作,则读取快照数据,(历史数据),)多版本控制??锁定读 ???select ... lock in share mode:对记录加 S 锁,其它事务也可以加S锁,如果加 x 锁则会被阻塞? S锁和X锁??? Read View :判断可见性,即其他事务对当前事务是否可见。 Next-key-Lock???
purge 操作?
通过 undo log 读取之前的版本数据,以此实现非锁定读,对同一个数据行的修改,最终undo log记录会形成一个链表,表头就是最新版本记录(ReadView+可读性算法分析时就是根据这个链表所记录的事务id 和 当前事务的id进行比较从而确认读取哪个历史记录)
一致性非锁定读&&锁定读
一致性非锁定读:
通常的做法是: 在每一行记录添加一个隐藏的版本号或时间戳,更新时该行的版本号+1(或时间戳改变),读取时,当前可见的版本号与对应记录的版本号进行比对,如果记录的版本小于可见版本,则表示该记录可见。
在RC和RR两个隔离级别的情况下,select(不包括 select ... lock in share mode ,select ... for update)会默认使用MVCC机制。
锁定读:锁定读的语句:
- insert、update、delete 操作
- select ... for update 、select ... lock in share mode
InnoDB对MVCC的实现:
MVCC 的实现依赖于:隐藏字段、Read View、undo log。隐藏字段中的DB_TRX_ID和ReadView实现当前数据是否对事务可见,如不可见,则通过数据行的 DB_ROLL_PTR 找到 undo log 中的历史版本。
隐藏字段:
DB_TRX_ID: 表示最后一次插入或更新该行的事务 id。此外,delete 操作在内部被视为更新,只不过会在记录头 Record header 中的 deleted_flag 字段将其标记为已删除DB_ROLL_PTR: 回滚指针,指向undo logDB_ROW_ID: 如果没有主键和唯一非空索引时会自动创建,并使用该id生成聚簇索引
Read View结构:
class ReadView {
/* ... */
private:
trx_id_t m_low_limit_id; /* 如果行记录大于等于这个 ID,
则 当前事务不可读取这个行号的数据, 值当前事务id+1 */
trx_id_t m_up_limit_id; /* 如果行上的DB_TRX_ID小于这个值,则当前事务可读取这个行,
活跃列表中的最小id,如果活跃列表为空则等于 m_low_limit_id */
trx_id_t m_creator_trx_id; /* 创建该 Read View 的事务ID */
trx_id_t m_low_limit_no; /* 事务 Number, 小于该 Number 的 Undo Logs 均可以被 Purge */
ids_t m_ids; /* 创建 Read View 时的活跃事务列表,当前未提交事务 ID 集合,
后续即使这些id里的事务修改了数据,对当前事务时不可见的*/
m_closed; /* 标记 Read View 是否 close */
}
undo log
用于事务回滚时恢复数据。当读取记录时,若该记录被其他事务占用或当前版本对该事务不可见,则可以通过 undo log 读取之前的版本数据,以此实现非锁定读。
undo log类型:
- insert undo log :
指在 insert 操作中产生的 undo log。因为 insert 操作的记录只对事务本身可见,对其他事务不可见,故该 undo log 可以在事务提交后直接删除。不需要进行 purge 操作 - update undo log:
update 或 delete 操作中产生的 undo log。该 undo log可能需要提供 MVCC 机制,因此不能在事务提交时就进行删除。提交时放入 undo log 链表,等待 purge线程 进行最后的删除.
RR和RC两个隔离级别下的MVCC实现差距
在事务隔离级别 RC 和 RR (InnoDB 存储引擎的默认事务隔离级别)下,InnoDB 存储引擎使用 MVCC(非锁定一致性读),但它们生成 Read View 的时机却不同在 **RC 隔离级别下的 每次select **查询前都生成一个Read View **(m_ids 列表)在 ==RR 隔离级别下只在事务开始后 第一次select ==数据前生成一个Read View(m_ids 列表)
MVCC+Next-key-Lock 防止幻读
1、执行普通 select,此时会以 MVCC 快照读的方式读取数据在快照读的情况下,RR 隔离级别只会在事务开启后的第一次查询生成 Read View ,并使用至事务提交。所以在生成 Read View 之后其它事务所做的更新、插入记录版本对当前事务并不可见,实现了可重复读和防止快照读下的 “幻读”
2、执行 select...for update/lock in share mode、insert、update、delete 等当前读在当前读下,读取的都是最新的数据,如果其它事务有插入新的记录,并且刚好在当前事务查询范围内,就会产生幻读 !InnoDB 使用 Next-key Lock 来防止这种情况。当执行当前读时,会锁定读取到的记录的同时,锁定它们的间隙,防止其它事务在查询范围内插入数据。