多版本并发控制 (Multi-Version Concurrency Control)
MVCC 是一种并发控制机制,用于在多个并发事务同时读写数据库时保持数据的一致性和隔离性。它是通过在每个数据行上维护多个版本的数据来实现的。当一个事务要对数据库中的数据进行修改时,MVCC 会为该事务创建一个数据快照,而不是直接修改实际的数据行。
1、读操作(SELECT):
当一个事务执行读操作时,它会使用快照读取。快照读取是基于事务开始时数据库中的状态创建的,因此事务不会读取其他事务尚未提交的修改。具体工作情况如下:
- 对于读取操作,事务会查找符合条件的数据行,并选择符合其事务开始时间的数据版本进行读取。
- 如果某个数据行有多个版本,事务会选择不晚于其开始时间的最新版本,确保事务只读取在它开始之前已经存在的数据。
- 事务读取的是快照数据,因此其他并发事务对数据行的修改不会影响当前事务的读取操作。
2、写操作(INSERT、UPDATE、DELETE):
当一个事务执行写操作时,它会生成一个新的数据版本,并将修改后的数据写入数据库。具体工作情况如下:
- 对于写操作,事务会为要修改的数据行创建一个新的版本,并将修改后的数据写入新版本。
- 新版本的数据会带有当前事务的版本号,以便其他事务能够正确读取相应版本的数据。
- 原始版本的数据仍然存在,供其他事务使用快照读取,这保证了其他事务不受当前事务的写操作影响。
3、事务提交和回滚:
- 当一个事务提交时,它所做的修改将成为数据库的最新版本,并且对其他事务可见。
- 当一个事务回滚时,它所做的修改将被撤销,对其他事务不可见。
4、版本的回收:
- 为了防止数据库中的版本无限增长,MVCC 会定期进行版本的回收。回收机制会删除已经不再需要的旧版本数据,从而释放空间。
MVCC 通过创建数据的多个版本和使用快照读取来实现并发控制。读操作使用旧版本数据的快照,写操作创建新版本,并确保原始版本仍然可用。这样,不同的事务可以在一定程度上并发执行,而不会相互干扰,从而提高了数据库的并发性能和数据一致性。
InnoDB 对 MVCC 的实现
MVCC
的实现依赖于:隐藏字段、Read View、undo log。在内部实现中,InnoDB
通过数据行的 DB_TRX_ID
和 Read View
来判断数据的可见性,如不可见,则通过数据行的 DB_ROLL_PTR
找到 undo log
中的历史版本。每个事务读到的数据版本可能是不一样的,在同一个事务中,用户只能看到该事务创建 Read View
之前已经提交的修改和该事务本身做的修改
隐藏字段
在内部,InnoDB
存储引擎为每行数据添加了三个 隐藏字段:
DB_TRX_ID(6字节)
:表示最后一次插入或更新该行的事务 id。此外,delete
操作在内部被视为更新,只不过会在记录头 Record header
中的 deleted_flag
字段将其标记为已删除DB_ROLL_PTR(7字节)
回滚指针,指向该行的 undo log
。如果该行未被更新,则为空DB_ROW_ID(6字节)
:如果没有设置主键且该表没有唯一非空索引时,InnoDB
会使用该 id 来生成聚簇索引
ReadView
主要是用来做可见性判断,里面保存了 “当前对本事务不可见的其他活跃事务”
主要有以下字段:
m_low_limit_id
:目前出现过的最大的事务 ID+1,即下一个将被分配的事务 ID。大于等于这个 ID 的数据版本均不可见m_up_limit_id
:活跃事务列表 m_ids
中最小的事务 ID,如果 m_ids
为空,则 m_up_limit_id
为 m_low_limit_id
。小于这个 ID 的数据版本均可见m_ids
:Read View
创建时其他未提交的活跃事务 ID 列表。创建 Read View
时,将当前未提交事务 ID 记录下来,后续即使它们修改了记录行的值,对于当前事务也是不可见的。m_ids
不包括当前事务自己和已提交的事务(正在内存中)m_creator_trx_id
:创建该 Read View
的事务 ID
undo-log
undo log
主要有两个作用:
- 当事务回滚时用于将数据恢复到修改前的样子
- 另一个作用是
MVCC
,当读取记录时,若该记录被其他事务占用或当前版本对该事务不可见,则可以通过 undo log
读取之前的版本数据,以此实现非锁定读
参考资料