简单MySQL例子演示MVCC
一沈秋园,满庭霜落,云烟北桥夜连城
MVCC 是多版本并发控制的缩写,是一种数据库和编程语言中常用的并发控制方法。它通过保存数据的历史版本,实现对数据库的高效访问。
MySQL 中 MVCC 主要是通过行记录中的隐藏字段(隐藏主键 row_id,事务ID trx_id,回滚指针 roll_pointer),undo_log(版本链),ReadView(一致性试图)来实现的。
下面是一个简单的例子:
假设有一个表 t,有两个字段 id 和 name,初始状态如下:
id | name |
1 | A |
2 | B |
现在有两个事务 T1 和 T2,T1 在可重复读(Repeatable Read)隔离级别下,T2 在读已提交(Read Committed)隔离级别下。
T1 和 T2 的执行过程如下:
1,T1 开始,执行 select * from t; 得到结果:
id | name |
1 | A |
2 | B |
2,T2 开始,执行 update t set name = 'C' where id = 1; 此时表 t 的行记录发生变化,变为:
id | name | row_id | trx_id | roll_pointer |
1 | C | 100 | 200 | 300 |
2 | B | 101 | NULL | NULL |
此时 undo log 中记录了 id 为 1 的行记录的旧版本:
id | name | row_id | trx_id | roll_pointer |
1 | A | 300 | NULL | NULL |
3,T1 再次执行 select * from t; 此时由于 T1 是可重复读隔离级别,它会根据自己的 ReadView 来判断那些记录是可见的。
ReadView中保存了当前活跃事务的 ID 列表,以及当前系统最小和最大事务ID。
假设 T1 的 ReadView 如下:
min_trx_id | max_trx_id | active_trx_id |
100 | 200 | [200] |
那么T1会根据以下规则来判断行记录是否可见:
- 如果行记录的trx_id为NULL,或者小于min_trx_id,说明该行记录是在T1之前就已经提交的,对T1可见。
- 如果行记录的trx_id等于T1自己的trx_id,说明该行记录是T1自己修改的,对T1可见。
- 如果行记录的trx_id大于max_trx_id,说明该行记录是在T1之后才修改的,对T1不可见。
- 如果行记录的trx_id在min_trx_id和max_trx_id之间,并且不在active_trx_ids中,说明该行记录是在T1之前就已经提交的,对T1可见。
- 如果行记录的trx_id在min_trx_id和max_trx_id之间,并且在active_trx_ids中,说明该行记录是由其他未提交事务修改的,对T1不可见。
根据这些规则,T1会发现 id 为1的行记录不可见,因为它的 trx_id 为 200,在 active_trx_ids 中。所以 T1 会沿着回滚指针找到 undo log 中的旧版本,并判断它是否可见。
旧版本的 trx_id 为NULL,小于 min_trx_id,所以对 T1 可见。因此,T1 得到结果:
id | name |
1 | A |
2 | B |
这就是MVCC的原理,通过保存数据的历史版本,实现对数据库的高效访问,保证事务的一致性和隔离性。
接下来看T2的执行过程:
4,T2 执行 commit; 提交事务,此时表 t 的行记录变为:
id | name | row_id | trx_id | roll_pointer |
1 | C | 100 | NULL | NULL |
2 | B | 101 | NULL | NULL |
undo log中的旧版本被删除。
5,T2再次执行 select * from t;
,此时由于 T2 是读已提交隔离级别,它会根据自己的 ReadView 来判断哪些行记录是可见的。
ReadView 中保存了当前活跃事务的 ID列表,以及当前系统最小和最大事务ID。
假设 T2 的 ReadView 如下:
min_trx_id | max_trx_id | active_trx_id |
201 | 201 | [] |
那么T2会根据以下规则来判断行记录是否可见:
- 如果行记录的 trx_id 为 NULL,或者小于 min_trx_id,说明该行记录是在 T2 之前就已经提交的,对 T2 可见。
- 如果行记录的 trx_id 等于 T2 自己的trx_id,说明该行记录是 T2 自己修改的,对 T2 可见。
- 如果行记录的 trx_id 大于 max_trx_id,说明该行记录是在 T2 之后才修改的,对 T2 不可见。
- 如果行记录的 trx_id 在 min_trx_id 和 max_trx_id 之间,并且不在 active_trx_ids 中,说明该行记录是在 T2 之前就已经提交的,对 T2 可见。
- 如果行记录的 trx_id 在 min_trx_id 和 max_trx_id 之间,并且在 active_trx_ids 中,说明该行记录是由其他未提交事务修改的,对 T2 不可见。
根据这些规则,T2 会发现 id 为 1 和 id 为 2 的行记录都可见,因为它们的 trx_id 都为 NULL,小于 min_trx_id。因此,T2 得到结果:
id | name |
1 | C |
2 | B |
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· DeepSeek 开源周回顾「GitHub 热点速览」
· 物流快递公司核心技术能力-地址解析分单基础技术分享
· .NET 10首个预览版发布:重大改进与新特性概览!
· AI与.NET技术实操系列(二):开始使用ML.NET
· .NET10 - 预览版1新功能体验(一)
2019-05-29 冲刺第十三天