MySQL-事务(下)-MySQL事务隔离级别与MVCC
目录
一、事务隔离级别
(一)不同隔离级别与问题的关系
(二)MySQL默认隔离级别
二、MVCC:多版本并发控制的奥秘
(一)MVCC的前提知识
1. 3个记录隐藏字段
2. undo日志
3. Read View
(二)MVCC的工作流程
(三)Read View与可见性判断
三、RR与RC的本质区别
四、总结
在数据库的世界里,事务是保证数据一致性的核心机制,而事务隔离级别和多版本并发控制(MVCC)则是事务处理中极为关键的技术点。今天,我们就来深入探究MySQL中的事务隔离级别以及MVCC的工作原理。
一、事务隔离级别
事务隔离级别定义了多个事务并发执行时,一个事务能看到其他事务操作的程度。MySQL有四种隔离级别,从低到高分别是读未提交(Read Uncommitted)、读已提交(Read Committed)、可重复读(Repeatable Read)和可串行化(Serializable)。
(一)不同隔离级别与问题的关系
隔离级别 脏读 不可重复读 幻读 加锁读
- 脏读:一个事务读取到另一个事务未提交的修改数据。
- 不可重复读:一个事务内多次读取同一数据,结果因其他事务修改而不同(重点是修改和删除操作导致)。
- 幻读:一个事务内多次查询,结果集因其他事务新增记录而不同(重点是新增记录导致)。
(二)MySQL默认隔离级别
MySQL默认的隔离级别是可重复读(Repeatable Read)。这是一个比较均衡的选择,它能避免脏读、不可重复读和幻读问题,同时相比可串行化隔离级别,并发性能更好。一般情况下,不建议修改这个默认设置,因为它在大多数场景下能很好地平衡数据安全性和数据库并发性能。
二、MVCC:多版本并发控制的奥秘
多版本并发控制(MVCC)是MySQL在可重复读等隔离级别下,实现高效并发读操作的关键技术。它通过维护数据的多个版本,使得读操作不需要加锁(或减少锁的使用),从而提高数据库的并发性能。
(一)MVCC的前提知识
要理解MVCC,需要先了解以下三个关键概念:
1. 3个记录隐藏字段
- DB_TRX_ID:6字节,记录最近修改(修改/插入)该记录的事务ID。
- DB_ROLL_PTR:7字节,回滚指针,指向该记录的上一个版本(这些版本数据一般存储在undo log中)。
- DB_ROW_ID:6字节,隐含的自增ID(隐藏主键),如果数据表没有主键,InnoDB会自动以DB_ROW_ID产生一个聚簇索引。
此外,还有一个删除flag隐藏字段,记录被更新或删除时,并非真正删除,而是删除flag发生变化。
2. undo日志
undo log可以简单理解为MySQL中的一段内存缓冲区,用来保存旧数据。当事务需要回滚或者MVCC需要获取历史版本数据时,就会用到undo log中的内容。
3. Read View
Read View是事务进行快照读操作时产生的读视图。在事务执行快照读的那一刻,会生成数据库系统当前的一个快照,记录并维护系统当前活跃事务的ID(每个事务开启时,都会被分配一个递增的ID)。Read View本质是用来进行可见性判断的,即判断当前事务能够看到哪个版本的数据(可能是当前最新数据,也可能是undo log里的某个历史版本数据)。
(二)MVCC的工作流程
以一个简单的例子来模拟MVCC的工作过程:
假设我们有一个 student 表,结构如下:
CREATE TABLE IF NOT EXISTS student(name VARCHAR(11) NOT NULL,age INT NOT NULL
);
插入一条记录:
INSERT INTO student (name, age) VALUES ('张三', 28);
此时,该记录的隐藏字段情况大致为: DB_TRX_ID 为 null (表示创建该记录的事务ID,这里假设为初始状态), DB_ROW_ID 为 1 , DB_ROLL_PTR 为 null (因为没有历史版本)。
步骤 | 事务 10 操作流程 | 事务 11 操作流程 |
---|---|---|
1 | 开始执行,准备修改 student 表中 name 为 “张三” 的记录 | - |
2 | 给目标记录加锁 | - |
3 | 将该记录拷贝到 undo log 中(写时拷贝) | - |
4 | 修改原始记录的 name 为 “李四” | - |
5 | 修改原始记录隐藏字段 DB_TRX_ID 为事务 10 的 ID | - |
6 | 修改原始记录回滚指针 DB_ROLL_PTR ,指向 undo log 中副本数据的地址 | - |
7 | 事务 10 提交,释放锁 | - |
8 | - | 开始执行,准备修改 student 表中 name 为 “李四” 的记录 |
9 | - | 给目标记录加锁 |
10 | - | 将该记录(当前为 “李四” 的记录)拷贝到 undo log 中 |
11 | - | 修改原始记录的 age 为 38 |
12 | - | 修改原始记录隐藏字段 DB_TRX_ID 为事务 11 的 ID |
13 | - | 修改原始记录回滚指针 DB_ROLL_PTR ,指向 undo log 中刚拷贝的副本数据地址 |
14 | - | 事务 11 提交,释放锁 |
现在,我们就有了一个基于链表记录的历史版本链,所谓的回滚,就是用历史数据覆盖当前数据。
(三)Read View与可见性判断
当事务进行快照读时,会生成Read View,然后根据Read View中的信息(如当前活跃事务ID集合、高水位、低水位等)来判断数据版本的可见性。
例如:
Read View中有 m_ids (Read View生成时刻系统正活跃的事务ID列表)、
up_limit_id ( m_ids 列表中事务ID最小的值)、
low_limit_id (Read View生成时刻系统尚未分配的下一个事务ID,即目前已出现过的事务ID的最大值+1)、
creator_trx_id (创建该Read View的事务ID)等信息。
在实际读取数据版本链时,会获取到每个版本对应的事务ID(即记录的 DB_TRX_ID ),然后根据Read View中的这些信息来判断该版本是否对当前事务可见。如果当前版本不可见,就通过回滚指针遍历下一个版本,直到找到可见的版本或者遍历完版本链。
三、RR与RC的本质区别
可重复读(RR)和读已提交(RC)隔离级别最本质的区别在于Read View生成时机的不同:
- 在RC隔离级别下,事务中每次快照读都会生成并获取最新的Read View。这就导致在RC级别下,事务内的多次快照读可能会看到不同的结果,因此RC级别存在不可重复读问题。
- 在RR隔离级别下,同一个事务中的第一次快照读会创建一个Read View,之后该事务后续的快照读都使用同一个Read View。所以,只要当前事务在其他事务提交更新之前使用过快照读,那么之后的快照读使用的都是同一个Read View,对之后的修改不可见,从而避免了不可重复读问题。
四、总结
事务隔离级别和MVCC是MySQL事务处理中非常重要的内容。事务隔离级别决定了事务间的隔离程度,平衡了数据安全性和并发性能;而MVCC则是在特定隔离级别下,实现高效并发读操作的核心技术。理解它们的工作原理,对于我们设计和优化数据库应用,以及排查数据库并发问题都有着至关重要的作用。