MVCC(多版本并发机制)
文章目录
简介
MySQL 中的 MVCC(Multi-Version Concurrency Control,多版本并发控制)是一种用于实现高并发、低冲突数据访问的技术。它通过维护数据的多个版本来实现读写操作的并发控制,它允许多个事务同时对同一数据进行读取和修改,而不会互相干扰,从而实现了非阻塞的读操作,降低了写操作的冲突。MVCC 在 InnoDB 存储引擎中得到了实现,是实现 ACID(原子性、一致性、隔离性、持久性)属性中隔离性的关键。
在并发场景下,可能出现三种情况:读-读并发
,读-写并发
,写-写并发
。在没有写的情况下 读-读并发
是不会出现问题的,而 写-写并发
这种情况比较常用的就是通过加锁的方式实现。那么,读-写并发
则可以通过 MVCC 的机制解决。
工作原理
MVCC 的基本思想是通过存储每行数据的多个版本,实现并发控制,允许读操作和写操作并发执行,避免大部分加锁操作。
主要组件
- 隐藏列 :
trx_id
:每行记录的创建或最后一次修改该行数据的事务ID。roll_pointer
:指向该行数据的上一个版本的指针,用于形成版本链。
- 回滚日志(Undo Log) :
- 每次事务修改数据时,旧版本数据会被保存在回滚日志中,形成一个版本链。
- 用于事务回滚和快照读时的数据读取。
- 读视图(Read View) :
- 事务启动时生成的快照视图,用于确定在事务期间可见的数据版本。
- 确保在可重复读隔离级别下读取到一致性数据。
事务隔离级别与 MVCC
不同的事务隔离级别对 MVCC 的行为有不同的要求:
- 读未提交(Read Uncommitted) :
- 不使用 MVCC,读取未提交的数据,可能会出现脏读。
- 读已提交(Read Committed) :
- 每次读取最新提交的数据版本(即一个事务中的每一次
SELECT
都会重新获取一次ReadView
),不使用快照视图,可能会出现不可重复读。
- 每次读取最新提交的数据版本(即一个事务中的每一次
- 可重复读(Repeatable Read) :
- 使用 MVCC,事务在启动时创建一致性视图,读取该视图的快照数据(即一个事务中只有第一次
SELECT
会读取ReadView
),避免不可重复读和幻读问题。
- 使用 MVCC,事务在启动时创建一致性视图,读取该视图的快照数据(即一个事务中只有第一次
- 串行化(Serializable) :
- 强制事务串行执行,通过加锁实现,不使用 MVCC。
实现方式
快照读(Snapshot Read)
快照读是指读取一致性视图中的数据,不加锁。具体流程如下:
- 事务启动:在事务启动时,InnoDB 创建一个一致性视图(Read View),记录当前系统中活跃事务的 ID。
- 读取数据 :读取数据时,根据隐藏列
trx_id
和roll_pointer
形成的版本链,查找符合当前一致性视图的数据版本。 - 返回结果:返回符合一致性视图的数据版本,确保读取的是事务启动时的快照数据。
sql
-- 事务1:启动事务
START TRANSACTION;
-- 快照读,读取一致性视图中的数据
SELECT * FROM example WHERE id = 1;
当前读(Current Read)
当前读是指读取最新的数据版本并加锁,确保数据的一致性。具体操作包括:
- SELECT ... FOR UPDATE:读取数据并加排他锁,阻止其他事务的读写操作。
- SELECT ... LOCK IN SHARE MODE:读取数据并加共享锁,允许其他事务的读操作,但阻止写操作。
- INSERT、UPDATE、DELETE:对数据进行修改,必须读取最新版本的数据并加锁。
SQL
-- 事务1:启动事务
START TRANSACTION;
-- 当前读,加排他锁
SELECT * FROM example WHERE id = 1 FOR UPDATE;
可以说,快照读是 MVCC 实现的基础,而当前读是悲观锁实现的基础。
版本链管理
每次数据修改操作时,InnoDB 会创建一个新版本的记录,并通过 roll_pointer
指向旧版本,形成版本链。
sql
-- 事务2:启动事务并更新数据
START TRANSACTION;
UPDATE example SET value = 'B' WHERE id = 1;
COMMIT;
在上述操作中,InnoDB 会将旧版本的 value = 'A'
存储在回滚日志中,新版本的 value = 'B'
记录当前事务ID,并指向旧版本。
MVCC 的优缺点
优点
- 高并发性能:读操作无需加锁,减少锁争用,提高系统并发性能。
- 数据一致性:通过一致性视图,实现可重复读和读已提交隔离级别下的数据一致性,避免脏读和不可重复读问题。
缺点
- 存储开销:维护多个版本的数据和回滚日志,增加存储需求。
- 数据清理:需要定期清理无用的旧版本数据,增加系统维护复杂性。
示例
以下示例展示了 MVCC 在可重复读隔离级别下的工作方式:
sql
-- 创建表并插入数据
CREATE TABLE example (
id INT PRIMARY KEY,
value VARCHAR(50)
);
INSERT INTO example (id, value) VALUES (1, 'A');
-- 事务1:启动事务并读取数据
SET TRANSACTION ISOLATION LEVEL REPEATABLE READ;
START TRANSACTION;
SELECT * FROM example WHERE id = 1; -- 读取数据,创建一致性视图
-- 事务2:启动事务并更新数据
SET TRANSACTION ISOLATION LEVEL READ COMMITTED;
START TRANSACTION;
UPDATE example SET value = 'B' WHERE id = 1;
COMMIT;
-- 事务1:再次读取数据
SELECT * FROM example WHERE id = 1; -- 仍然读取旧版本数据 'A'
COMMIT;
在这个示例中,事务1 启动后创建的一致性视图确保其读取的数据在事务期间保持不变,即使事务2 更新了数据。
总结
MySQL 中的 MVCC 机制通过维护数据的多个版本,实现高并发、低冲突的数据访问。它通过隐藏列、回滚日志和一致性视图等机制,在确保数据一致性的同时,提高了系统的并发性能。通过理解 MVCC 的工作原理和实现方式,可以更好地应用和优化 MySQL 数据库。
延伸
- MVCC 机制中的版本链管理是如何实现的
- 快照读是如何实现的(快照如何存储?存储在哪?多个快照存在时怎么读?)