MVCC(多版本并控制)
概念:MVCC 是一种并发控制机制。
作用:多个并发事务同时读写数据库时保持数据的一致性和隔离性
实现:在每个数据行上维护多个版本的数据来实现的。当一个事务要对数据库中的数据进行修改时,MVCC 会为该事务创建一个数据快照,而不是直接修改实际的数据行。
1、读操作
事务执行读操作的时候,会使用快照读取,事务不会读取其他事务尚未提交的修改。
2、写操作
事务执行写操作的时候,会生成一个新的数据版本,并将修改后的数据写入数据库。(原始版本的数据仍然存在,供其他事务使用快照读取)
3、事务提交和回滚
- 事务提交后,所做的修改将成为数据库的最新版本,并且对其他事务可见。
- 事务回滚时,所做的修改将被撤销,对其他事务不可见。
4、版本回收
防止数据库中的版本无限增长,MVCC会定期进行版本回收,从而释放空间。
总结:MVCC创建多个版本 和 使用快照读取来实现并发控制。读操作使用旧数据版本的快照,写操作创建新版本,并确保原始版本仍可以使用 。
一致性非锁定读
实现:加一个版本号 / 时间戳字段,查询时,将当前可见的版本号与对应记录的版本号进行对比,如果记录的版本号小于可见版本,则表示该记录可见。
多版本控制就是读非锁定读的实现。如果读取的行正在执行DELETE
或UPDATE
操作,这时读取操作不会去等待行上锁的释放,InnoDB
存储引擎会去读取行的快照(快照读)
锁定读
在锁定读下,读取的数据是数据的最新版本,这种读也被称为当前读
,同时,锁定读会对读取到的记录加锁。
存在问题:如果是当前读
,每次读取的都是最新数据,两次查询中间,如果有其他事务插入数据,就会导致幻读,所以,InnoDB
在实现可重复读时,如果执行的是当前读,则会对读取的记录使用Next-key Lock
,来防止其他事务在间隙间插入数据。
InnoDB 对MVCC的实现
在内部,InnoDB
存储引擎为每行数据添加了三个隐藏字段。
DB_TRX_ID(6字节)
:表示最后一次插入或更新该行的事务 id。此外,delete
操作在内部被视为更新,只不过会在记录头Record header
中的deleted_flag
字段将其标记为已删除DB_ROLL_PTR(7字节)
回滚指针,指向该行的undo log
。如果该行未被更新,则为空DB_ROW_ID(6字节)
:如果没有设置主键且该表没有唯一非空索引时,InnoDB
会使用该 id 来生成聚簇索引
Undo-log(撤销日志)
作用:
事务回滚
:事务回滚时用于将数据恢复到修改前的样子MVCC
:当读取记录时,若该记录被其他事务占用或当前版本对该事务不可见,则可以通过undo log
读取之前的版本数据,以此实现非锁定读
RC 和 RR隔离级别下MVCC的差异
在事务隔离级别 RC
和 RR
(InnoDB 存储引擎的默认事务隔离级别)下,InnoDB
存储引擎使用 MVCC
(非锁定一致性读),但它们生成 Read View
的时机却不同
- 在 RC 隔离级别下的
每次select
查询前都生成一个Read View
(m_ids 列表) - 在 RR 隔离级别下只在事务开始后
第一次select
数据前生成一个Read View
(m_ids 列表)
MVCC + Next-key Lock 防止幻读
InnoDB
存储引擎在 RR 级别下通过 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 来防止这种情况。当执行当前读时,会锁定读取到的记录的同时,锁定它们的间隙,防止其它事务在查询范围内插入数据。只要我不让你插入,就不会发生幻读