Skip to content
体验新版
项目
组织
正在加载...
登录
切换导航
打开侧边栏
wushizhenking
JavaGuide
提交
d96ba4e8
J
JavaGuide
项目概览
wushizhenking
/
JavaGuide
与 Fork 源项目一致
从无法访问的项目Fork
通知
2
Star
0
Fork
0
代码
文件
提交
分支
Tags
贡献者
分支图
Diff
Issue
0
列表
看板
标记
里程碑
合并请求
0
Wiki
0
Wiki
分析
仓库
DevOps
项目成员
Pages
J
JavaGuide
项目概览
项目概览
详情
发布
仓库
仓库
文件
提交
分支
标签
贡献者
分支图
比较
Issue
0
Issue
0
列表
看板
标记
里程碑
合并请求
0
合并请求
0
Pages
分析
分析
仓库分析
DevOps
Wiki
0
Wiki
成员
成员
收起侧边栏
关闭侧边栏
动态
分支图
创建新Issue
提交
Issue看板
前往新版Gitcode,体验更适合开发者的 AI 搜索 >>
提交
d96ba4e8
编写于
8月 10, 2021
作者:
G
guide
浏览文件
操作
浏览文件
下载
电子邮件补丁
差异文件
Update InnoDB对MVCC的实现.md
上级
452fc49d
变更
1
隐藏空白更改
内联
并排
Showing
1 changed file
with
19 addition
and
19 deletion
+19
-19
docs/database/mysql/InnoDB对MVCC的实现.md
docs/database/mysql/InnoDB对MVCC的实现.md
+19
-19
未找到文件。
docs/database/mysql/InnoDB对MVCC的实现.md
浏览文件 @
d96ba4e8
...
...
@@ -18,7 +18,7 @@
## 一致性非锁定读和锁定读
###
#
一致性非锁定读
### 一致性非锁定读
对于
[
**一致性非锁定读(Consistent Nonlocking Reads)**
](
https://dev.mysql.com/doc/refman/5.7/en/innodb-consistent-read.html
)
的实现,通常做法是加一个版本号或者时间戳字段,在更新数据的同时版本号 + 1 或者更新时间戳。查询时,将当前可见的版本号与对应记录的版本号进行比对,如果记录的版本小于可见版本,则表示该记录可见
...
...
@@ -26,13 +26,13 @@
在
`Repeatable Read`
和
`Read Committed`
两个隔离级别下,如果是执行普通的
`select`
语句(不包括
`select ... lock in share mode`
,
`select ... for update`
)则会使用
`一致性非锁定读(MVCC)`
。并且在
`Repeatable Read`
下
`MVCC`
实现了可重复读和防止部分幻读
###
#
锁定读
### 锁定读
如果执行的是下列语句,就是
[
**锁定读(Locking Reads)**
](
https://dev.mysql.com/doc/refman/5.7/en/innodb-locking-reads.html
)
-
select ... lock in share mode
-
select ... for update
-
insert、update、delete
操作
-
`select ... lock in share mode`
-
`select ... for update`
-
`insert`
、
`update`
、
`delete`
操作
在锁定读下,读取的是数据的最新版本,这种读也被称为
`当前读(current read)`
。锁定读会对读取到的记录加锁:
...
...
@@ -40,13 +40,13 @@
-
`select ... for update`
、
`insert`
、
`update`
、
`delete`
:对记录加
`X`
锁,且其它事务不能加任何锁
在一致性非锁定读下,即使读取的记录已被其它事务加上
`X`
锁,这时记录也是可以被读取的,即读取的快照数据。上面说了
在
`Repeatable Read`
下
`MVCC`
防止了部分幻读,这边的 “部分” 是指在
`一致性非锁定读`
情况下,只能读取到第一次查询之前所插入的数据(根据 Read View 判断数据可见性,Read View 在第一次查询时生成),但如果是
`当前读`
,每次读取的都是最新数据,这时如果两次查询中间有其它事务插入数据,就会产生幻读。
**所以
`InnoDB` 在实现`Repeatable Read` 时,如果执行的是当前读,则会对读取的记录使用 `Next-key Lock` ,来防止其它事务在间隙间插入数据**
在一致性非锁定读下,即使读取的记录已被其它事务加上
`X`
锁,这时记录也是可以被读取的,即读取的快照数据。上面说了
,在
`Repeatable Read`
下
`MVCC`
防止了部分幻读,这边的 “部分” 是指在
`一致性非锁定读`
情况下,只能读取到第一次查询之前所插入的数据(根据 Read View 判断数据可见性,Read View 在第一次查询时生成)。但是!如果是
`当前读`
,每次读取的都是最新数据,这时如果两次查询中间有其它事务插入数据,就会产生幻读。所以,
**
`InnoDB` 在实现`Repeatable Read` 时,如果执行的是当前读,则会对读取的记录使用 `Next-key Lock` ,来防止其它事务在间隙间插入数据**
## InnoDB 对 MVCC 的实现
`MVCC`
的实现依赖于:
**隐藏字段、Read View、undo log**
。在内部实现中,
`InnoDB`
通过数据行的
`DB_TRX_ID`
和
`Read View`
来判断数据的可见性,如不可见,则通过数据行的
`DB_ROLL_PTR`
找到
`undo log`
中的历史版本。每个事务读到的数据版本可能是不一样的,在同一个事务中,用户只能看到该事务创建
`Read View`
之前已经提交的修改和该事务本身做的修改
###
#
隐藏字段
### 隐藏字段
在内部,
`InnoDB`
存储引擎为每行数据添加了三个
[
隐藏字段
](
https://dev.mysql.com/doc/refman/5.7/en/innodb-multi-versioning.html
)
:
...
...
@@ -54,7 +54,7 @@
-
`DB_ROLL_PTR(7字节)`
回滚指针,指向该行的
`undo log`
。如果该行未被更新,则为空
-
`DB_ROW_ID(6字节)`
:如果没有设置主键且该表没有唯一非空索引时,
`InnoDB`
会使用该 id 来生成聚簇索引
###
#
ReadView
### ReadView
[
`Read View`
](
https://github.com/facebook/mysql-8.0/blob/8.0/storage/innobase/include/read0types.h#L298
)
主要是用来做可见性判断,里面保存了 “当前对本事务不可见的其他活跃事务”
...
...
@@ -65,7 +65,7 @@
-
`m_ids`
:
`Read View`
创建时其他未提交的活跃事务 ID 列表。创建
`Read View`
时,将当前未提交事务 ID 记录下来,后续即使它们修改了记录行的值,对于当前事务也是不可见的。
`m_ids`
不包括当前事务自己和已提交的事务(正在内存中)
-
`m_creator_trx_id`
:创建该
`Read View`
的事务 ID
###
#
undo-log
### undo-log
`undo log`
主要有两个作用:
...
...
@@ -78,21 +78,21 @@
**`insert` 时的数据初始状态:**
![
markdown
](
https://ddmcc-1255635056.file.myqcloud.com/317e91e1-1ee1-42ad-9412-9098d5c6a9ad.png
)
![](
https://ddmcc-1255635056.file.myqcloud.com/317e91e1-1ee1-42ad-9412-9098d5c6a9ad.png
)
2.
**`update undo log`**
:
`update`
或
`delete`
操作中产生的
`undo log`
。该
`undo log`
可能需要提供
`MVCC`
机制,因此不能在事务提交时就进行删除。提交时放入
`undo log`
链表,等待
`purge线程`
进行最后的删除
**数据第一次被修改时:**
![
markdown
](
https://ddmcc-1255635056.file.myqcloud.com/c52ff79f-10e6-46cb-b5d4-3c9cbcc1934a.png
)
![](
https://ddmcc-1255635056.file.myqcloud.com/c52ff79f-10e6-46cb-b5d4-3c9cbcc1934a.png
)
**数据第二次被修改时:**
![
markdown
](
https://ddmcc-1255635056.file.myqcloud.com/6a276e7a-b0da-4c7b-bdf7-c0c7b7b3b31c.png
)
![](
https://ddmcc-1255635056.file.myqcloud.com/6a276e7a-b0da-4c7b-bdf7-c0c7b7b3b31c.png
)
不同事务或者相同事务的对同一记录行的修改,会使该记录行的
`undo log`
成为一条链表,链首就是最新的记录,链尾就是最早的旧记录
###
#
数据可见性算法
### 数据可见性算法
在
`InnoDB`
存储引擎中,创建一个新事务后,执行每个
`select`
语句前,都会创建一个快照(Read View),
**快照中保存了当前数据库系统中正处于活跃(没有 commit)的事务的 ID 号**
。其实简单的说保存的是系统中当前不应该被本事务看到的其他事务 ID 列表(即 m_ids)。当用户在这个事务中要读取某个记录行的时候,
`InnoDB`
会将该记录行的
`DB_TRX_ID`
与
`Read View`
中的一些变量及当前事务 ID 进行比较,判断是否满足可见性条件
...
...
@@ -127,13 +127,13 @@
举个例子:
![
markdown
](
https://ddmcc-1255635056.file.myqcloud.com/6fb2b9a1-5f14-4dec-a797-e4cf388ed413.png
)
![](
https://ddmcc-1255635056.file.myqcloud.com/6fb2b9a1-5f14-4dec-a797-e4cf388ed413.png
)
###
# **在 RC 下 ReadView 生成情况**
###
在 RC 下 ReadView 生成情况
1.
**`假设时间线来到 T4 ,那么此时数据行 id = 1 的版本链为`:**
!
[
markdown
](
https://ddmcc-1255635056.file.myqcloud.com/a3fd1ec6-8f37-42fa-b090-7446d488fd04.png
)
!
[](
https://ddmcc-1255635056.file.myqcloud.com/a3fd1ec6-8f37-42fa-b090-7446d488fd04.png
)
由于 RC 级别下每次查询都会生成
`Read View`
,并且事务 101、102 并未提交,此时
`103`
事务生成的
`Read View`
中活跃的事务
**`m_ids` 为:[101,102]**
,
`m_low_limit_id`
为:104,
`m_up_limit_id`
为:101,
`m_creator_trx_id`
为:103
...
...
@@ -159,7 +159,7 @@
> **总结:** **在 RC 隔离级别下,事务在每次查询开始时都会生成并设置新的 Read View,所以导致不可重复读**
###
# **在 RR 下 ReadView 生成情况**
###
在 RR 下 ReadView 生成情况
**在可重复读级别下,只会在事务开始后第一次读取数据时生成一个 Read View(m_ids 列表)**
...
...
@@ -199,11 +199,11 @@
`InnoDB`
存储引擎在 RR 级别下通过
`MVCC`
和
`Next-key Lock`
来解决幻读问题:
1.
**
执行普通 `select`,此时会以 `MVCC` 快照读的方式读取数据**
**1、
执行普通 `select`,此时会以 `MVCC` 快照读的方式读取数据**
在快照读的情况下,RR 隔离级别只会在事务开启后的第一次查询生成
`Read View`
,并使用至事务提交。所以在生成
`Read View`
之后其它事务所做的更新、插入记录版本对当前事务并不可见,实现了可重复读和防止快照读下的 “幻读”
2.
**
执行 select...for update/lock in share mode、insert、update、delete 等当前读**
**2、
执行 select...for update/lock in share mode、insert、update、delete 等当前读**
在当前读下,读取的都是最新的数据,如果其它事务有插入新的记录,并且刚好在当前事务查询范围内,就会产生幻读!
`InnoDB`
使用
[
Next-key Lock
](
https://dev.mysql.com/doc/refman/5.7/en/innodb-locking.html#innodb-next-key-locks
)
来防止这种情况。当执行当前读时,会锁定读取到的记录的同时,锁定它们的间隙,防止其它事务在查询范围内插入数据。只要我不让你插入,就不会发生幻读
...
...
编辑
预览
Markdown
is supported
0%
请重试
或
添加新附件
.
添加附件
取消
You are about to add
0
people
to the discussion. Proceed with caution.
先完成此消息的编辑!
取消
想要评论请
注册
或
登录