MVCC (Multi-Version Concurrency Control),多版本并发控制。数据库实现并发访问请求,就是基于 MVCC 实现的。
首先了解下数据库事物的隔离级别。
隔离级别
- Read Uncommiteed 读未提交 读取未提交的数据,即其他事物已经修改但还未提交的数据,这是最低的隔离级别。
- Read committed 读已提交 读取已提交的数据。在一个事物中,对同一条数据,可能会出现读取不一致现象。
- Repeatable Read 可重复读 可重复读取,在一个事物中,对同一条数据,确保多次读取的结果一样。(Mysql 默认隔离级别)
- Serializable 序列化 串行执行,数据库中的事物都是串行执行,不能并行执行,效率最差。
隔离级别主要是为了实现读操作不需要加锁, 从而提高数据库的性能。
不同隔离级别出现的问题
隔离级别 | 脏读 | 幻读 | 不可重复读 |
---|---|---|---|
读未提交 | ✅ | ✅ | ✅ |
读已提交 | ✅ | ✅ | |
可重复度 | ✅ | ||
序列化 |
Read Uncommiteed 和 Serializable 不需要使用多版本控制技术就可实现。 Read Uncommiteed :直接修改原始数据即可,其他事物查看数据的时候直接可以查看到,无须其他操作即可实现。 Serializable: 所有的事物都是串行执行的,只需要一个独占锁即可实现。
其中Read committed 和 Repeatable Read 两种事物隔离使用到 MVCC 进行实现的。
MVCC 实现原理
MVCC (Multi-Version Concurrency Control),多版本并发控制。MVCC 对每行数据维护多个版本。可以实现读操作不需要加锁,即可实现在同一个事物中多次读取一条数据,结果都是一致的。
MySql InnoDB存储引擎为例,InnoDB 在表中增加了两个隐藏字段。每个事物都是一个事物ID,其中一个列存储了修改时的事物ID,另一个列存储的是删除这条数据的事物ID。每开启一个事物都会生成一个自增的事物ID,当查询一条数据时,都会用当前的事物ID,和隐藏列中的事物ID进行对比,然后根据不同的事物隔离级别来决定是否返回该行数据。
下面对 Select、Delete、 Insert、 Update 四种操作了解 MVCC 的实现原理
创建一个表,表中有两个字段 id、name。MVCC 中增加两个隐藏列 update_version、delete_version。 update_version: 存储修改时,当前事物的ID delete_version: 存储删除时,当前事物的ID。
Insert
假设当前事物的ID=1
插入一条数据时,update_version 存储当前事物的ID,。
id | name | update_version | delete_version |
---|---|---|---|
1 | 张三 | 1 |
Update
当前事物ID=2
修改一条数据时,会新把原来的行复制一份,并把之前的那条数据delete_version 列设置成当前的事物ID 2,标记当前数据为删除,新插入的数据 update_version 设置成当前事物ID 2。
id | name | update_version | delete_version |
---|---|---|---|
1 | 张三 | 1 | 2 |
1 | 张小三 | 2 |
Delete
当前事物ID=4
删除数据时,直接把 delete_version 这个字段设置为当前的事物ID 4.
id | name | update_version | delete_version |
---|---|---|---|
1 | 张三 | 1 | 2 |
1 | 张小三 | 2 | 4 |
Select
查询必须满足以下两种条件才能返回:
- 行的 update_version 版本号小于等于当前事物ID
- 行的 delete_version 版本无值或大于当前事物ID
Select 示例一
比如事物ID=3, 查询 id=1 的数据
id | name | update_version | delete_version |
---|---|---|---|
1 | 张三 | 1 | 2 |
1 | 张小三 | 2 |
查询条件为:id=1 and update_version<=3 and delete_version>3 查询出来的数据应该是 “张小三” 这条数据。
Select 示例二
假设,事物ID 3 执行的查询时间比较长,这时事物4开始删除这条数据,并提交,然后事物ID=3 的查询才开始执行,应该是能查询到 id=1 的数据。
事物ID=4 删除后的数据。
id | name | update_version | delete_version |
---|---|---|---|
1 | 张三 | 1 | 2 |
1 | 张小三 | 2 | 4 |
查询条件为:id=1 and update_version<=3 and delete_version>3 查询结果应该为 “张三” 这条数据。