Mysql高级篇(中)——多版本并发控制 MVCC
多版本并发控制 MVCC
- 一、概述
- 二、基本原理
- 三、实现原理
- 四、示例解释
- 五、MVCC 优点
- 六、现实中的实现
- 七、MVCC 三剑客
- 1. ReadView
- 2. Undo Log
- 3. Purge
- 4. 三者之间的关系:
- 5. 示例
- 6. 总结
- 八、MVCC 整体操作流程
- ⭐、readview
- 1. 作用
- 2. 工作机制
- 3. 数据版本的可见性判断规则
- 4. 应用场景
- 5. 示例
- 6. 总结
- ⭐、隐藏字段
一、概述
多版本并发控制
(MVCC,Multi-Version Concurrency Control
)是一种用于管理数据库系统中并发事务的技术,主要目的是在多个事务同时执行时,提供一致性读操作并避免冲突。它通过为每个事务创建不同的“数据版本”,实现读写并发,从而使得读操作不会阻塞写操作,反之亦然。
二、基本原理
在MVCC
系统中,每次事务对数据进行修改时,数据库会为数据生成一个新的版本,而不是直接覆盖旧的版本。这样,旧版本的数据仍然保留,用于满足并发读请求。每个事务在读取数据时,能够根据其开始的时间戳看到与其一致的版本,而不会被其他事务的修改所影响。
- 写事务: 当一个事务对数据进行写操作时,它会创建一个新的版本。新的版本不会影响正在进行的读事务,直到该写事务提交。
- 读事务: 读操作可以读取提交前的旧版本数据,以保证读取的一致性,即事务看到的数据是一致的,不会被其他未提交的修改所影响。
三、实现原理
多版本并发控制
(MVCC
)的 实现原理 基于以下几个核心概念和机制,它们共同工作以实现高效的并发控制和事务隔离。
四、示例解释
假设有一个账户余额数据库,账户的初始余额是100。现有两个事务并发执行:
❤ 事务A:在余额上加50元。
❤ 事务B:查询账户余额。
⭐ 操作顺序:
1、事务A读取余额(100元),并准备增加50元。
2、事务B同时启动,读取余额,它看到的是100元(因为事务A的操作尚未提交,事务B只会看到旧版本的余额)。
3、事务A提交,将余额更新为150元。
4、事务B继续进行操作,它读取的仍然是余额100元,而不是事务A提交的150元,因为事务B在读取时,事务A的修改还未生效。
在这个过程中,事务B看到的余额是事务开始时的一致性快照(即100元),而不是事务A提交后的150元。这保证了事务B的读操作与事务A的写操作不冲突。
五、MVCC 优点
非阻塞读:
读操作不会被写操作阻塞,反之亦然。即使有多个事务同时修改数据,读事务可以无锁地执行。提高并发性:
由于事务可以并发执行,数据库的吞吐量得以提高。事务隔离:
MVCC通过创建多个版本的数据,可以为事务提供更好的隔离性,尤其是读未提交、读已提交和可重复读这几种隔离级别。
六、现实中的实现
许多现代数据库系统,如PostgreSQL
和MySQL
的InnoDB
引擎,都实现了MVCC
。以MySQL
的InnoDB
为例:
- 每条记录有两个额外的隐藏列,分别存储了 创建该版本的事务ID 和 删除该版本的事务ID 。当一个事务读取记录时,数据库会根据这些ID来判断该事务是否能够看到该记录的版本。
- 快照读: 事务读取时基于该事务启动时的快照,可以避免读取到其他事务未提交的修改。
七、MVCC 三剑客
MVCC
三剑客是指MVCC
机制中与事务和并发控制密切相关的 三个核心概念,分别是ReadView
、Undo Log
和Purge
。这三者共同作用,确保多版本并发控制能够高效地管理数据的一致性和并发性。
1. ReadView
ReadView
是在事务读取数据时生成的一个一致性视图,它记录了当前活跃的事务及其事务ID
,用于决定哪些数据版本对当前事务可见。
ReadView
的作用:
- 一致性读:
ReadView
确保事务读取到的数据是一致的,即使有其他事务正在对数据进行修改,事务只会看到与其一致性视图相关的版本。 - 事务隔离:
ReadView
通过记录当前未提交的事务,帮助事务实现“快照隔离
”(Snapshot Isolation
)或“可重复读
”(Repeatable Read
)等隔离级别。
ReadView
的工作方式:
- 当事务第一次执行查询时,系统会生成一个 ReadView,记录当前所有活跃事务的事务ID列表。
- 根据这些事务ID,决定当前事务读取到的数据版本。事务只能看到在其开始之前已经提交的数据版本,未提交的修改或后续的修改对当前事务不可见。
2. Undo Log
Undo Log
是事务修改数据时生成的日志,它记录了每次修改之前的数据状态,以便在事务回滚时能够撤销这些修改,恢复到原始状态。
Undo Log
的作用:
- 事务回滚:当事务回滚时,系统利用
Undo Log
将修改的数据恢复到未修改之前的状态。 - 版本控制:在
MVCC
中,Undo Log
也用于保存旧版本的数据,使得并发事务能够读取已提交的历史数据。
Undo Log
的工作方式:
- 每当一个事务修改数据时,
InnoDB
会生成一个Undo Log
记录,保存修改前的旧版本数据。 - 如果事务需要回滚,
Undo Log
中的数据可以用来撤销修改,恢复到修改前的状态。 Undo Log
还用于构建历史版本数据,允许并发事务读取旧版本。
例如,假设事务A
对记录 R
进行修改,将其值从 100
改为 150
。在修改时,Undo Log
会保存原始值 100
,以便回滚时能够恢复。
3. Purge
Purge
是 InnoDB
的一种后台清理机制,用于删除不再需要的旧版本数据和 Undo Log
记录,释放存储空间。
Purge
的作用:
- 回收空间:当某个旧版本的数据不再需要被事务访问时,
Purge
会将这些版本数据从存储中删除,以释放磁盘空间。 - 清理 Undo Log:如果某条记录的
Undo Log
记录不再需要(即所有相关的事务都已提交或回滚),Purge 进程会将这些日志删除。
Purge
的工作方式:
InnoDB
定期会有一个后台进程负责清理不再需要的历史版本和Undo Log
。Purge
过程保证旧版本的数据不会无限增长,防止存储空间耗尽。Purge
进程会根据事务的活跃情况,清理那些不再被任何事务引用的历史版本。只有当某个旧版本的数据和Undo Log
对所有事务都不可见时,才会被清理。
4. 三者之间的关系:
- ReadView 确保事务读取到一致的快照,并通过事务
ID
列表确定哪些版本对事务可见。 - Undo Log 保存事务的修改前状态,用于支持回滚操作和历史版本数据的读取。
- Purge 定期清理不再需要的旧版本和
Undo Log
,确保数据库的存储空间得到有效利用。
5. 示例
假设有两个事务A和B同时对一条记录 R 进行操作:
1、事务A:开始并读取记录 R,此时 ReadView 生成,记录了当前系统中的活跃事务状态。
2、事务B:修改记录 R 的值并创建 Undo Log,保存修改前的旧值。事务B未提交时,事务A读取的仍然是旧版本数据。
3、事务B:提交后,新版本的 R 对其他事务可见。如果事务A的 ReadView 是在事务B提交之前生成的,它依然会读取旧版本数据。
4、Purge:在事务A结束后,系统中的其他事务也结束,事务B创建的旧版本不再需要,后台 Purge 进程将清理掉这些旧版本和对应的 Undo Log。
通过 ReadView、Undo Log 和 Purge 的协作,系统实现了多版本并发控制,确保数据读取一致性、事务回滚能力和存储空间的高效利用。
6. 总结
MVCC
三剑客(ReadView
、Undo Log
、Purge
)是MVCC
机制中不可或缺的三个核心组件。它们分别负责确保事务读取一致的快照、提供数据版本的回滚能力、以及清理不再需要的旧版本数据,从而实现高效的并发控制和空间管理。
总结:
MVCC
通过创建数据的多个版本,避免了读写冲突,提升了数据库系统的并发性能。其 核心思想 是:读操作读取旧版本数据,写操作创建新版本数据,从而实现非阻塞的并发控制
。
八、MVCC 整体操作流程
多版本并发控制
(MVCC)的整体操作流程通过创建和维护数据的多个版本,确保数据库中多个事务能够并发执行,提供一致性读和事务隔离。下面详细介绍MVCC的整体操作流程,涵盖事务启动、读取、写入、提交和回滚等操作。
操作流程 | 简述 |
---|---|
1. 事务启动 : | 分配事务ID ,准备执行操作 |
2. 读操作 : | 通过 ReadView 生成快照,确保读取数据的一致性 |
3. 写操作 : | 创建新版本而不影响现有版本,保证读写并发 |
4. 提交操作 : | 新版本提交后对其他事务可见 |
5. 回滚操作 : | 撤销事务的修改,恢复旧版本 |
6. 并发控制 : | 通过版本判断实现读写并发,同时使用锁机制处理写写冲突 |
7. 垃圾回收 : | 清理旧版本,释放存储空间 |
⭐、readview
在
多版本并发控制
(MVCC)系统中,ReadView 是一个 用于实现一致性读 的概念。它是在事务执行过程中生成的一个快照,用于决定当前事务可以看到哪些数据版本。特别是在MySQL
的InnoDB
存储引擎中,ReadView
负责管理事务在读取数据时能看到的数据状态。
1. 作用
当一个事务开始进行查询操作时,InnoDB
会创建一个 ReadView
,它记录了当前所有活跃事务的快照状态,并帮助事务决定哪些数据版本是对其可见的,哪些版本应该被忽略。
主要作用是:
- 确保事务读取的数据是一致的,即使在该事务执行期间有其他事务对数据进行了修改。
- 提供一致性读(Consistent Read),即事务在读取时看到的数据是该事务开始时的快照,而不会被其他未提交的修改影响。
2. 工作机制
在创建 ReadView
时,InnoDB
记录了系统中当前所有活跃的事务,特别是这些事务的事务ID
(Transaction ID
,trx_id
)。接下来,系统通过以下规则来决定哪些数据版本是可见的:
trx_id 列表
ReadView
包含以下几个重要的事务ID
相关的信息:
m_ids
(活跃事务ID
列表):所有在创建ReadView
时未提交的事务ID。这些事务对当前事务来说是“活跃的”,其修改的版本数据不可见。min_trx_id
:m_ids
中最小的事务ID
。任何小于这个ID
的数据版本都对当前事务可见,因为它们在当前事务开始之前已经提交。max_trx_id
:ReadView
创建时分配的最大事务ID
,任何大于或等于这个ID
的事务都是在当前事务之后开始的,它们的修改数据对当前事务不可见。
通过这些信息,InnoDB
可以为每个查询提供一个一致的视图,让事务看到的是当前快照中的数据,而不是其他未提交事务的修改。
3. 数据版本的可见性判断规则
在决定一个数据版本是否对当前事务可见时,系统会按照以下步骤来检查该版本的事务ID
(trx_id
):
- 如果
trx_id
<min_trx_id
,表示该版本是由比所有活跃事务都早的事务创建的,因此它已经提交,可以被当前事务看到。 - 如果
trx_id
inm_ids
中,表示该版本的创建事务仍然活跃,尚未提交,因此该版本不可见。 - 如果
trx_id
>=max_trx_id
,表示该版本是由在当前事务之后开始的事务创建的,因此不可见。
通过这些规则,ReadView
确保了每个事务只看到在它开始之前已经提交的数据版本,从而实现一致性读。
4. 应用场景
ReadView
通常在以下场景中发挥作用:
可重复读(Repeatable Read)隔离级别
: 在这个隔离级别下,事务从开始到结束,看到的都是同一个快照。ReadView
是在第一次读取操作时创建的,之后的每次读取操作都基于这个快照,从而保证可重复读。
快照隔离(Snapshot Isolation)
: 类似于可重复读,事务看到的是它开始时的快照,通过 ReadView 实现。
5. 示例
6. 总结
ReadView
是 MVCC
的核心组件之一,用于管理事务的快照状态。 它通过记录当前系统中的活跃事务列表,帮助决定哪些数据版本对当前事务可见,哪些版本不可见。ReadView
确保了事务在并发执行时能够看到一致的数据快照,从而实现了非阻塞的并发控制。
⭐、隐藏字段
在使用
多版本并发控制
(MVCC)的数据库系统中,隐藏字段 是一些额外的元数据字段,它们在用户的表结构中是不可见的,但对数据库的内部操作至关重要。隐藏字段通常用于存储与多版本控制和事务管理相关的信息,以帮助数据库系统实现高效的并发控制、数据版本管理和事务隔离。以下是这些隐藏字段的详细说明,主要以MySQL
的InnoDB
引擎为例。
1. DB_TRX_ID(事务ID)
DB_TRX_ID
是每条记录的事务ID
字段,用于记录最后修改该记录的事务ID
。
- 作用:当一个事务对某条记录进行修改时,
InnoDB
会将该事务的事务ID
(Transaction ID
,trx_id
)写入该字段,表示这是由哪个事务最后修改的版本。这个字段对于判断记录的可见性非常重要,因为每个事务只会读取到在其开始之前提交的事务生成的版本。 - 用途:在读取记录时,数据库根据读取事务的事务ID与该记录的事务ID进行比较,决定该版本是否对当前事务可见。
举例:
假设事务A
对记录 R
进行了修改,事务A
的ID
是 10,那么 R
的 DB_TRX_ID
字段将被更新为 10。当另一个事务B
(事务ID为 15)读取这条记录时,数据库会根据 DB_TRX_ID
判断事务B
是否能看到该版本。
2. DB_ROLL_PTR(回滚指针)
DB_ROLL_PTR
是一个回滚指针,用来指向与该记录相关的Undo Log
记录。通过这个字段,数据库可以追溯到该记录的历史版本。
作用: DB_ROLL_PTR
用于指向该记录的 Undo Log
中的日志,Undo Log
保存了记录在被修改之前的状态。因此,通过回滚指针,数据库可以找到记录的旧版本数据,从而在事务回滚或并发读取时获取历史版本。
用途: 这个字段在事务回滚和读取旧版本时非常有用。通过它,数据库可以沿着 Undo Log
追溯到历史版本,以支持 MVCC
的一致性读操作。
举例:
当事务A
修改了记录 R
后,InnoDB
会为这次修改生成一条 Undo Log
,并更新 R
的 DB_ROLL_PTR
字段,指向这条 Undo Log
。如果需要回滚或读取旧版本数据,系统会通过这个指针找到对应的 Undo Log
来获取旧值。
3. DB_ROW_ID(行ID)
DB_ROW_ID
是一个唯一标识每条记录的行ID
。在没有主键的表中,InnoDB
会为每条记录自动分配一个行ID
,作为记录的唯一标识。
作用: 即使表中没有显式的主键字段,InnoDB
仍然需要一个唯一的标识符来区分每条记录。DB_ROW_ID
是一个自增的数字,用于唯一标识表中的每条记录。
用途: 如果表没有定义主键,InnoDB
会使用 DB_ROW_ID
作为内部的聚簇索引键。虽然用户看不到这个字段,但它在内部用于对表数据进行唯一定位。
举例:
如果用户创建了一张没有主键的表,InnoDB
会自动为每条记录分配一个 DB_ROW_ID
,这个值会递增。即使用户没有显式地看到该字段,它在表的内部存储中用于唯一标识每条记录。
隐藏字段的工作机制
在
InnoDB
引擎中,隐藏字段
对MVCC
实现至关重要。它们通过提供事务ID
、回滚指针
和行ID
等信息,帮助系统管理数据的多个版本并实现以下功能:
- 事务隔离: 通过
DB_TRX_ID
来判断每条记录的版本是否对当前事务可见,从而确保不同事务之间的数据读取不会相互影响。- 版本回溯: 通过
DB_ROLL_PTR
,数据库能够追溯到记录的历史版本,从而支持并发读操作和事务回滚。- 记录唯一性: 通过
DB_ROW_ID
,即使表中没有定义主键,InnoDB 仍能唯一标识每条记录。
例子:隐式字段在事务中的作用
假设有两个并发事务A和B:
1、事务A:修改了一条记录 R
,将值从 100
改为 150
。同时,事务A
的事务ID
10
被写入该记录的 DB_TRX_ID
字段,并创建了一个 Undo Log
,保存原来的值 100
。记录的 DB_ROLL_PTR
更新为指向这个 Undo Log
。
2、事务B:同时读取记录 R
,事务B
的事务ID
为 15
。由于事务A
尚未提交,事务B
会通过 DB_TRX_ID
检查该记录的修改是否对其可见。系统发现 DB_TRX_ID
小于事务B
的事务ID
,因此事务B
会读取修改前的版本,即通过 DB_ROLL_PTR
指向的 Undo Log
,获取到值 100
。
3、事务A 提交后:DB_TRX_ID
仍然记录事务A
的ID 10
,但此时事务A
的修改已经提交,后续的事务读取 R
时将直接看到修改后的值 150
。
总结
隐藏字段
在MVCC
实现中起着至关重要的作用。它们虽然对用户不可见,但它们存储了关于事务和版本控制的关键信息,帮助数据库系统有效地管理数据版本、提供一致性读、支持事务回滚以及提升并发性能。