MySQL中的MVCC机制指的是什么?
MVCC(多版本并发控制)是一种机制,允许多个事务同时运行而不互相干扰。每次修改数据时,MySQL 不会直接覆盖原数据,而是创建一个新的数据版本。这样,每个事务看到的都是它开始时的数据**“快照”**,避免了事务间的冲突,从而提升了数据库的并发能力。
📚 知识内容:
🗃️ 1. 数据版本管理 :
理论:
MVCC 的核心思想是为每次修改的数据创建一个新的版本,而不是直接覆盖原有的数据。每个数据版本都有一个标识,通常是事务 ID(trx_id)或时间戳⏰。每次数据修改都会生成一个新的版本,原来的版本仍然存在,直到事务提交。多个事务可以并发操作数据,每个事务只能看到自己启动时的“快照”数据,不会受到其他事务修改的影响。
举例:
假设有一个表 employees,包含 id 和 salary 字段:
CREATE TABLE employees (
id INT PRIMARY KEY,
salary DECIMAL(10, 2)
);
事务 1 执行了 UPDATE 操作,修改了 Alice 的薪资:
– 事务 1 修改数据
START TRANSACTION;
UPDATE employees SET salary = 50000 WHERE id = 1;
同时,事务 2 执行查询操作:
– 事务 2 查询数据
START TRANSACTION;
SELECT * FROM employees WHERE id = 1;
在 MVCC 中,事务 1 更新了数据,但在事务 2 查询时,事务 2 看到的是事务 1 修改前的数据版本,直到事务 1 提交后,事务 2 才能看到事务 1 提交后的新版本。📅
📜 2. Undo Log 的作用 :
理论:
Undo Log 记录了事务的每一个修改操作的“反向操作”,它使得事务可以回滚到修改前的状态。Undo Log 不直接存储所有的数据版本,而是存储“撤销操作”⏪。通过 Undo Log,MySQL 可以恢复数据到先前的状态,即便是事务失败或被回滚时。
举例:
假设事务 1 插入了一条数据,后来决定回滚:
– 事务 1 插入数据
START TRANSACTION;
INSERT INTO employees (id, salary) VALUES (1, 40000);
– 事务回滚
ROLLBACK;
在事务 1 执行 ROLLBACK 时,Undo Log 会存储这次插入的反向操作(删除这条记录)。Undo Log 通过撤销这次插入操作,将数据恢复到未插入之前的状态。🔄
👀 3. 数据版本的访问与可见性 :
理论:
MVCC 中,每个事务都有自己的视图,通过 readView 来判断哪些数据版本对当前事务可见。readView 记录了当前所有活跃事务的 ID,以及最小和最大事务 ID。当事务执行查询操作时,MySQL 根据 readView 判断它能看到的数据版本。
举例:
假设事务 1 和事务 2 在并发执行:
事务 1 修改了数据,并提交。
事务 2 在事务 1 提交之前查询了数据。
事务 2 的 readView 会记录事务 1 尚未提交的版本。因此,在事务 2 查询数据时,它会看到事务 1 提交前的数据版本,直到事务 1 提交后,事务 2 才能看到新的版本数据.
– 事务 1 修改数据
START TRANSACTION;
UPDATE employees SET salary = 55000 WHERE id = 1;
COMMIT;
– 事务 2 查询数据
START TRANSACTION;
SELECT * FROM employees WHERE id = 1; – 事务 2 看到事务 1 提交前的薪资
👓 4. Read View 和可见性判断 :
理论:
readView 是 MVCC 的核心组件,帮助每个事务判断哪些数据版本对其可见。readView 中包含:
creator_trx_id:当前事务的事务 ID。
m_ids:活跃事务的事务 ID 列表。
min_trx_id:所有活跃事务中最小的事务 ID。
max_trx_id:为下一个事务分配的事务 ID。
通过 readView,MySQL 判断数据版本的 trx_id 是否对当前事务可见。若数据版本的 trx_id 小于 min_trx_id,且该版本已提交,则可见;若在 min_trx_id 和 max_trx_id 之间,且未提交,则不可见。
举例:
假设有事务 1 和事务 2,它们在处理相同数据:
事务 1 修改了数据并提交。
事务 2 在事务 1 提交前查询数据。
事务 2 的 readView 会记录事务 1 尚未提交,因此它看到的是事务 1 提交前的版本。当事务 1 提交后,事务 2 才能看到新的数据版本。
– 事务 1 修改数据
START TRANSACTION;
UPDATE employees SET salary = 60000 WHERE id = 1;
COMMIT;
– 事务 2 查询数据(可重复读)
START TRANSACTION;
SELECT * FROM employees WHERE id = 1; – 事务 2 看到事务 1 提交前的数据
🌱 知识拓展:
📖 快照读与当前读 :
理论:
快照读(Snapshot Read):事务在执行查询时,并不是直接读取最新的数据版本,而是读取它开始时的数据快照。即使其他事务在此期间修改了数据,事务依然能读取到开始时的数据。
当前读(Current Read):当前读则是直接读取数据库中最新的数据版本,并且会加锁,保证数据的一致性。当前读通常用于 UPDATE 或 DELETE 等操作。
举例:
假设事务 1 更新了某条记录,事务 2 查询这条记录:
事务 1 执行 UPDATE 时,修改了数据,生成新的数据版本。
事务 2 进行快照读时,会读取事务 1 修改前的版本,直到事务 1 提交。
– 事务 1 修改数据
START TRANSACTION;
UPDATE employees SET salary = 70000 WHERE id = 1;
– 事务 2 查询数据(快照读)
START TRANSACTION;
SELECT * FROM employees WHERE id = 1; – 事务 2 看到事务 1 修改前的数据
🔐 MVCC 与事务隔离级别 :
理论:
MVCC 的表现受事务隔离级别的影响。不同的隔离级别决定了事务如何读取数据、如何处理并发:
读已提交(Read Committed):每次查询时,生成新的 readView,只读取已提交事务的数据。事务会看到其他事务提交的数据,但可能会遇到“不可重复读”。
可重复读(Repeatable Read):整个事务使用相同的 readView,在事务期间读取的数据始终保持一致,避免了“不可重复读”问题。但可能会出现“幻读”。
串行化(Serializable):最严格的隔离级别,强制事务按顺序执行,避免了并发冲突,确保事务完全串行化,从而避免了所有并发问题。
举例:
假设事务 1 和事务 2 在 可重复读 隔离级别下并发执行:
事务 1 修改了数据并提交。
事务 2 在事务 1 提交前查询数据,读取的是事务 1 提交前的版本。
– 事务 1 修改数据
START TRANSACTION;
UPDATE employees SET salary = 75000 WHERE id = 1;
COMMIT;
– 事务 2 查询数据(可重复读)
START TRANSACTION;
SELECT * FROM employees WHERE id = 1; – 事务 2 看到事务 1 提交前的数据
🌀 幻读的解决 :
理论:
即使在可重复读隔离级别下,仍可能发生“幻读”。幻读是指在一个事务内执行多次查询时,其他事务可能会插入新记录,导致查询结果不一致。为了避免幻读,MySQL 提供了加锁机制,如 SELECT FOR UPDATE,它确保事务不会受到其他事务插入数据的影响。
举例:
假设事务 1 和事务 2 并发执行:
事务 1 查询了某个范围的数据。
事务 2 在事务 1 查询后插入了新记录,导致事务 1 的结果发生变化。
为了防止幻读,事务 1 可以通过 SELECT FOR UPDATE 加锁,确保其他事务无法在该范围内插入数据。
– 事务 1 查询数据并加锁
START TRANSACTION;
SELECT * FROM employees WHERE salary > 50000 FOR UPDATE;
– 事务 2 插入数据
START TRANSACTION;
INSERT INTO employees (id, salary) VALUES (3, 80000);
COMMIT;
🎉 总结:
MVCC 是 MySQL 中一项非常重要的技术,它通过为每个事务提供独立的视图(readView)来避免事务之间的冲突,同时支持高并发读操作。通过数据版本管理、Undo Log、和 readView 等机制,MVCC 确保了数据的一致性,同时提升了数据库的并发处理能力。🚀
