@@ -74,7 +74,7 @@ private:
74
74
75
75
** 事务可见性示意图** ([ 图源] ( https://leviathan.vip/2019/03/20/InnoDB%E7%9A%84%E4%BA%8B%E5%8A%A1%E5%88%86%E6%9E%90-MVCC/#MVCC-1 ) ):
76
76
77
- ![ trans_visible] ( https://leviathan.vip/2019/03/20/InnoDB%E7%9A%84%E4%BA%8B%E5%8A%A1%E5%88%86%E6%9E%90-MVCC/ trans_visible.jpg )
77
+ ![ trans_visible] ( ./images/mvvc/ trans_visible.png )
78
78
79
79
### undo-log
80
80
@@ -89,27 +89,27 @@ private:
89
89
90
90
** ` insert ` 时的数据初始状态:**
91
91
92
- ![ ] ( https://ddmcc-1255635056.file.myqcloud.com /317e91e1-1ee1-42ad-9412-9098d5c6a9ad.png)
92
+ ![ ] ( ./images/mvvc /317e91e1-1ee1-42ad-9412-9098d5c6a9ad.png)
93
93
94
94
2 . ** ` update undo log ` ** :` update ` 或 ` delete ` 操作中产生的 ` undo log ` 。该 ` undo log ` 可能需要提供 ` MVCC ` 机制,因此不能在事务提交时就进行删除。提交时放入 ` undo log ` 链表,等待 ` purge线程 ` 进行最后的删除
95
95
96
96
** 数据第一次被修改时:**
97
97
98
- ![ ] ( https://ddmcc-1255635056.file.myqcloud.com /c52ff79f-10e6-46cb-b5d4-3c9cbcc1934a.png)
98
+ ![ ] ( ./images/mvvc /c52ff79f-10e6-46cb-b5d4-3c9cbcc1934a.png)
99
99
100
100
** 数据第二次被修改时:**
101
101
102
- ![ ] ( https://ddmcc-1255635056.file.myqcloud.com /6a276e7a-b0da-4c7b-bdf7-c0c7b7b3b31c.png)
102
+ ![ ] ( ./images/mvvc /6a276e7a-b0da-4c7b-bdf7-c0c7b7b3b31c.png)
103
103
104
104
不同事务或者相同事务的对同一记录行的修改,会使该记录行的 ` undo log ` 成为一条链表,链首就是最新的记录,链尾就是最早的旧记录。
105
105
106
106
### 数据可见性算法
107
107
108
108
在 ` InnoDB ` 存储引擎中,创建一个新事务后,执行每个 ` select ` 语句前,都会创建一个快照(Read View),** 快照中保存了当前数据库系统中正处于活跃(没有 commit)的事务的 ID 号** 。其实简单的说保存的是系统中当前不应该被本事务看到的其他事务 ID 列表(即 m_ids)。当用户在这个事务中要读取某个记录行的时候,` InnoDB ` 会将该记录行的 ` DB_TRX_ID ` 与 ` Read View ` 中的一些变量及当前事务 ID 进行比较,判断是否满足可见性条件
109
109
110
- [ 具体的比较算法] ( https://github.com/facebook/mysql-8.0/blob/8.0/storage/innobase/include/read0types.h#L161 ) 如下: [ 图源] ( https://leviathan.vip/2019/03/20/InnoDB%E7%9A%84%E4%BA%8B%E5%8A%A1%E5%88%86%E6%9E%90-MVCC/#MVCC-1 )
110
+ [ 具体的比较算法] ( https://github.com/facebook/mysql-8.0/blob/8.0/storage/innobase/include/read0types.h#L161 ) 如下( [ 图源] ( https://leviathan.vip/2019/03/20/InnoDB%E7%9A%84%E4%BA%8B%E5%8A%A1%E5%88%86%E6%9E%90-MVCC/#MVCC-1 ) ):
111
111
112
- ![ ] ( https://ddmcc-1255635056.file.myqcloud.com /8778836b-34a8-480b-b8c7-654fe207a8c2.png)
112
+ ![ ] ( ./images/mvvc /8778836b-34a8-480b-b8c7-654fe207a8c2.png)
113
113
114
114
1 . 如果记录 DB_TRX_ID < m_up_limit_id,那么表明最新修改该行的事务(DB_TRX_ID)在当前事务创建快照之前就提交了,所以该记录行的值对当前事务是可见的
115
115
@@ -138,45 +138,45 @@ private:
138
138
139
139
举个例子:
140
140
141
- ![ ] ( https://ddmcc-1255635056.file.myqcloud.com /6fb2b9a1-5f14-4dec-a797-e4cf388ed413.png)
141
+ ![ ] ( ./images/mvvc /6fb2b9a1-5f14-4dec-a797-e4cf388ed413.png)
142
142
143
143
### 在 RC 下 ReadView 生成情况
144
144
145
- 1 . ** ` 假设时间线来到 T4 ,那么此时数据行 id = 1 的版本链为 ` :**
145
+ ** 1. 假设时间线来到 T4 ,那么此时数据行 id = 1 的版本链为:**
146
146
147
- ![ ] ( https://ddmcc-1255635056.file.myqcloud.com /a3fd1ec6-8f37-42fa-b090-7446d488fd04.png)
147
+ ![ ] ( ./images/mvvc /a3fd1ec6-8f37-42fa-b090-7446d488fd04.png)
148
148
149
149
由于 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
150
150
151
151
- 此时最新记录的 ` DB_TRX_ID ` 为 101,m_up_limit_id <= 101 < m_low_limit_id,所以要在 ` m_ids ` 列表中查找,发现 ` DB_TRX_ID ` 存在列表中,那么这个记录不可见
152
152
- 根据 ` DB_ROLL_PTR ` 找到 ` undo log ` 中的上一版本记录,上一条记录的 ` DB_TRX_ID ` 还是 101,不可见
153
153
- 继续找上一条 ` DB_TRX_ID ` 为 1,满足 1 < m_up_limit_id,可见,所以事务 103 查询到数据为 ` name = 菜花 `
154
154
155
- 2 . ** ` 时间线来到 T6 ,数据的版本链为 ` :**
155
+ ** 2. 时间线来到 T6 ,数据的版本链为:**
156
156
157
- ![ markdown ] ( https://ddmcc-1255635056.file.myqcloud.com /528559e9-dae8-4d14-b78d-a5b657c88391.png)
157
+ ![ ] ( ./images/mvvc /528559e9-dae8-4d14-b78d-a5b657c88391.png)
158
158
159
159
因为在 RC 级别下,重新生成 ` Read View ` ,这时事务 101 已经提交,102 并未提交,所以此时 ` Read View ` 中活跃的事务 ** ` m_ids ` :[ 102] ** ,` m_low_limit_id ` 为:104,` m_up_limit_id ` 为:102,` m_creator_trx_id ` 为:103
160
160
161
161
- 此时最新记录的 ` DB_TRX_ID ` 为 102,m_up_limit_id <= 102 < m_low_limit_id,所以要在 ` m_ids ` 列表中查找,发现 ` DB_TRX_ID ` 存在列表中,那么这个记录不可见
162
162
163
163
- 根据 ` DB_ROLL_PTR ` 找到 ` undo log ` 中的上一版本记录,上一条记录的 ` DB_TRX_ID ` 为 101,满足 101 < m_up_limit_id,记录可见,所以在 ` T6 ` 时间点查询到数据为 ` name = 李四 ` ,与时间 T4 查询到的结果不一致,不可重复读!
164
164
165
- 3 . ** ` 时间线来到 T9 ,数据的版本链为 ` :**
165
+ ** 3. 时间线来到 T9 ,数据的版本链为:**
166
166
167
- ![ markdown ] ( https://ddmcc-1255635056.file.myqcloud.com /6f82703c-36a1-4458-90fe-d7f4edbac71a.png)
167
+ ![ ] ( ./images/mvvc /6f82703c-36a1-4458-90fe-d7f4edbac71a.png)
168
168
169
169
重新生成 ` Read View ` , 这时事务 101 和 102 都已经提交,所以 ** m_ids** 为空,则 m_up_limit_id = m_low_limit_id = 104,最新版本事务 ID 为 102,满足 102 < m_low_limit_id,可见,查询结果为 ` name = 赵六 `
170
170
171
171
> ** 总结:** ** 在 RC 隔离级别下,事务在每次查询开始时都会生成并设置新的 Read View,所以导致不可重复读**
172
172
173
173
### 在 RR 下 ReadView 生成情况
174
174
175
- ** 在可重复读级别下,只会在事务开始后第一次读取数据时生成一个 Read View(m_ids 列表)**
175
+ 在可重复读级别下,只会在事务开始后第一次读取数据时生成一个 Read View(m_ids 列表)
176
176
177
- 1 . ** ` 在 T4 情况下的版本链为 ` :**
177
+ ** 1. 在 T4 情况下的版本链为:**
178
178
179
- ![ markdown ] ( https://ddmcc-1255635056.file.myqcloud.com /0e906b95-c916-4f30-beda-9cb3e49746bf.png)
179
+ ![ ] ( ./images/mvvc /0e906b95-c916-4f30-beda-9cb3e49746bf.png)
180
180
181
181
在当前执行 ` select ` 语句时生成一个 ` Read View ` ,此时 ** ` m_ids ` :[ 101,102] ** ,` m_low_limit_id ` 为:104,` m_up_limit_id ` 为:101,` m_creator_trx_id ` 为:103
182
182
@@ -186,11 +186,11 @@ private:
186
186
- 根据 ` DB_ROLL_PTR ` 找到 ` undo log ` 中的上一版本记录,上一条记录的 ` DB_TRX_ID ` 还是 101,不可见
187
187
- 继续找上一条 ` DB_TRX_ID ` 为 1,满足 1 < m_up_limit_id,可见,所以事务 103 查询到数据为 ` name = 菜花 `
188
188
189
- 2 . ** ` 时间点 T6 情况下 ` :**
189
+ ** 2. 时间点 T6 情况下:**
190
190
191
- ![ markdown ] ( https://ddmcc-1255635056.file.myqcloud.com /79ed6142-7664-4e0b-9023-cf546586aa39.png)
191
+ ![ ] ( ./images/mvvc /79ed6142-7664-4e0b-9023-cf546586aa39.png)
192
192
193
- 在 RR 级别下只会生成一次` Read View ` ,所以此时依然沿用 ** ` m_ids ` :[ 101,102] ** ,` m_low_limit_id ` 为:104,` m_up_limit_id ` 为:101,` m_creator_trx_id ` 为:103
193
+ 在 RR 级别下只会生成一次` Read View ` ,所以此时依然沿用 ** ` m_ids ` :[ 101,102] ** ,` m_low_limit_id ` 为:104,` m_up_limit_id ` 为:101,` m_creator_trx_id ` 为:103
194
194
195
195
- 最新记录的 ` DB_TRX_ID ` 为 102,m_up_limit_id <= 102 < m_low_limit_id,所以要在 ` m_ids ` 列表中查找,发现 ` DB_TRX_ID ` 存在列表中,那么这个记录不可见
196
196
@@ -200,9 +200,9 @@ private:
200
200
201
201
- 继续找上一条 ` DB_TRX_ID ` 为 1,满足 1 < m_up_limit_id,可见,所以事务 103 查询到数据为 ` name = 菜花 `
202
202
203
- 3 . ** 时间点 T9 情况下:**
203
+ ** 3. 时间点 T9 情况下:**
204
204
205
- ![ markdown ] ( https://ddmcc-1255635056.file.myqcloud.com /cbbedbc5-0e3c-4711-aafd-7f3d68a4ed4e.png)
205
+ ![ ] ( ./images/mvvc /cbbedbc5-0e3c-4711-aafd-7f3d68a4ed4e.png)
206
206
207
207
此时情况跟 T6 完全一样,由于已经生成了 ` Read View ` ,此时依然沿用 ** ` m_ids ` :[ 101,102] ** ,所以查询结果依然是 ` name = 菜花 `
208
208
0 commit comments