Update 数据库系统原理.md
重新绘制配图,把兼容矩阵和隔离级别对照改为表格,补充 ACID 的实现和幻读的示例,更新可串行化的描述。
|
After Width: | Height: | Size: 129 KiB |
|
After Width: | Height: | Size: 137 KiB |
|
After Width: | Height: | Size: 119 KiB |
|
After Width: | Height: | Size: 130 KiB |
|
After Width: | Height: | Size: 177 KiB |
|
After Width: | Height: | Size: 138 KiB |
|
After Width: | Height: | Size: 136 KiB |
|
After Width: | Height: | Size: 129 KiB |
|
After Width: | Height: | Size: 135 KiB |
|
After Width: | Height: | Size: 162 KiB |
|
After Width: | Height: | Size: 129 KiB |
|
After Width: | Height: | Size: 126 KiB |
@@ -49,7 +49,7 @@
|
||||
|
||||
事务指的是满足 ACID 特性的一组操作,可以通过 Commit 提交一个事务,也可以使用 Rollback 进行回滚。
|
||||
|
||||
<div align="center"> <img src="https://cs-notes-1256109796.cos.ap-guangzhou.myqcloud.com/image-20191207222237925.png"/> </div><br>
|
||||
<div align="center"> <img src="pics/txn-flow.png" width="760px"/> </div><br>
|
||||
|
||||
### ACID
|
||||
|
||||
@@ -61,7 +61,7 @@
|
||||
|
||||
#### 2. 一致性(Consistency)
|
||||
|
||||
数据库在事务执行前后都保持一致性状态。在一致性状态下,所有事务对同一个数据的读取结果都是相同的。
|
||||
数据库在事务执行前后都保持一致性状态,即满足所有的约束和业务规则。例如转账前后,两个账户的余额总和保持不变。
|
||||
|
||||
#### 3. 隔离性(Isolation)
|
||||
|
||||
@@ -82,7 +82,14 @@
|
||||
- 在并发的情况下,多个事务并行执行,事务不仅要满足原子性,还需要满足隔离性,才能满足一致性。
|
||||
- 事务满足持久化是为了能应对系统崩溃的情况。
|
||||
|
||||
<div align="center"> <img src="https://cs-notes-1256109796.cos.ap-guangzhou.myqcloud.com/image-20191207210437023.png"/> </div><br>
|
||||
在 InnoDB 中,这四个特性分别这样实现:
|
||||
|
||||
- 原子性:undo log 记录每次修改之前的值,回滚时用它恢复;
|
||||
- 持久性:修改数据页之前先写 redo log,默认配置下事务提交时把 redo log 刷到磁盘,崩溃后重放 redo log 恢复已提交的修改,见 [MySQL 的日志](MySQL.md#七日志);
|
||||
- 隔离性:写操作加锁,普通的读操作通过 MVCC 读取快照,见 [多版本并发控制](#五多版本并发控制);
|
||||
- 一致性:由以上三者和数据库的约束(主键、唯一索引、外键)共同保证,业务规则还需要应用自己维护。
|
||||
|
||||
<div align="center"> <img src="pics/acid.png" width="760px"/> </div><br>
|
||||
|
||||
### AUTOCOMMIT
|
||||
|
||||
@@ -90,31 +97,31 @@ MySQL 默认采用自动提交模式。也就是说,如果不显式使用`STAR
|
||||
|
||||
## 二、并发一致性问题
|
||||
|
||||
在并发环境下,事务的隔离性很难保证,因此会出现很多并发一致性问题。
|
||||
在并发环境下,事务的隔离性很难保证,因此会出现很多并发一致性问题。以下例子都是在 MySQL 8.0 上用两个会话实际执行得到的,表 account 中原有 id 为 1、2 的两行,余额都是 100。
|
||||
|
||||
### 丢失修改
|
||||
|
||||
丢失修改指一个事务的更新操作被另外一个事务的更新操作替换。一般在现实生活中常会遇到,例如:T<sub>1</sub> 和 T<sub>2</sub> 两个事务都对一个数据进行修改,T<sub>1</sub> 先修改并提交生效,T<sub>2</sub> 随后修改,T<sub>2</sub> 的修改覆盖了 T<sub>1</sub> 的修改。
|
||||
丢失修改指一个事务的更新操作被另外一个事务的更新操作替换。例如:T<sub>1</sub> 和 T<sub>2</sub> 先后读到余额为 100,T<sub>1</sub> 取出 30 元,写回 70 并提交;T<sub>2</sub> 存入 50 元,在它读到的 100 的基础上写回 150,T<sub>1</sub> 的修改被覆盖了。
|
||||
|
||||
<div align="center"> <img src="https://cs-notes-1256109796.cos.ap-guangzhou.myqcloud.com/image-20191207221744244.png"/> </div><br>
|
||||
<div align="center"> <img src="pics/txn-lost-update.png" width="780px"/> </div><br>
|
||||
|
||||
### 读脏数据
|
||||
|
||||
读脏数据指在不同的事务下,当前事务可以读到另外事务未提交的数据。例如:T<sub>1</sub> 修改一个数据但未提交,T<sub>2</sub> 随后读取这个数据。如果 T<sub>1</sub> 撤销了这次修改,那么 T<sub>2</sub> 读取的数据是脏数据。
|
||||
|
||||
<div align="center"> <img src="https://cs-notes-1256109796.cos.ap-guangzhou.myqcloud.com/image-20191207221920368.png"/> </div><br>
|
||||
<div align="center"> <img src="pics/txn-dirty-read.png" width="780px"/> </div><br>
|
||||
|
||||
### 不可重复读
|
||||
|
||||
不可重复读指在一个事务内多次读取同一数据集合。在这一事务还未结束前,另一事务也访问了该同一数据集合并做了修改,由于第二个事务的修改,第一次事务的两次读取的数据可能不一致。例如:T<sub>2</sub> 读取一个数据,T<sub>1</sub> 对该数据做了修改。如果 T<sub>2</sub> 再次读取这个数据,此时读取的结果和第一次读取的结果不同。
|
||||
|
||||
<div align="center"> <img src="https://cs-notes-1256109796.cos.ap-guangzhou.myqcloud.com/image-20191207222102010.png"/> </div><br>
|
||||
<div align="center"> <img src="pics/txn-non-repeatable.png" width="780px"/> </div><br>
|
||||
|
||||
### 幻影读
|
||||
|
||||
幻读本质上也属于不可重复读的情况,T<sub>1</sub> 读取某个范围的数据,T<sub>2</sub> 在这个范围内插入新的数据,T<sub>1</sub> 再次读取这个范围的数据,此时读取的结果和和第一次读取的结果不同。
|
||||
幻读本质上也属于不可重复读的情况,T<sub>2</sub> 读取某个范围的数据,T<sub>1</sub> 在这个范围内插入新的数据,T<sub>2</sub> 再次读取这个范围的数据,此时读取的结果和第一次读取的结果不同。
|
||||
|
||||
<div align="center"> <img src="https://cs-notes-1256109796.cos.ap-guangzhou.myqcloud.com/image-20191207222134306.png"/> </div><br>
|
||||
<div align="center"> <img src="pics/txn-phantom.png" width="780px"/> </div><br>
|
||||
|
||||
----
|
||||
|
||||
@@ -147,7 +154,10 @@ MySQL 中提供了两种封锁粒度:行级锁以及表级锁。
|
||||
|
||||
锁的兼容关系如下:
|
||||
|
||||
<div align="center"> <img src="https://cs-notes-1256109796.cos.ap-guangzhou.myqcloud.com/image-20191207213523777.png"/> </div><br>
|
||||
| | X | S |
|
||||
| :---: | :---: | :---: |
|
||||
| X | 冲突 | 冲突 |
|
||||
| S | 冲突 | 兼容 |
|
||||
|
||||
#### 2. 意向锁
|
||||
|
||||
@@ -164,7 +174,12 @@ MySQL 中提供了两种封锁粒度:行级锁以及表级锁。
|
||||
|
||||
各种锁的兼容关系如下:
|
||||
|
||||
<div align="center"> <img src="https://cs-notes-1256109796.cos.ap-guangzhou.myqcloud.com/image-20191207214442687.png"/> </div><br>
|
||||
| | X | IX | S | IS |
|
||||
| :---: | :---: | :---: | :---: | :---: |
|
||||
| X | 冲突 | 冲突 | 冲突 | 冲突 |
|
||||
| IX | 冲突 | 兼容 | 冲突 | 兼容 |
|
||||
| S | 冲突 | 冲突 | 兼容 | 兼容 |
|
||||
| IS | 冲突 | 兼容 | 兼容 | 兼容 |
|
||||
|
||||
解释如下:
|
||||
|
||||
@@ -181,7 +196,7 @@ MySQL 中提供了两种封锁粒度:行级锁以及表级锁。
|
||||
|
||||
可以解决丢失修改问题,因为不能同时有两个事务对同一个数据进行修改,那么事务的修改就不会被覆盖。
|
||||
|
||||
<div align="center"> <img src="https://cs-notes-1256109796.cos.ap-guangzhou.myqcloud.com/image-20191207220440451.png"/> </div><br>
|
||||
<div align="center"> <img src="pics/lock-level-1.png" width="780px"/> </div><br>
|
||||
|
||||
**二级封锁协议**
|
||||
|
||||
@@ -189,7 +204,7 @@ MySQL 中提供了两种封锁粒度:行级锁以及表级锁。
|
||||
|
||||
可以解决读脏数据问题,因为如果一个事务在对数据 A 进行修改,根据 1 级封锁协议,会加 X 锁,那么就不能再加 S 锁了,也就是不会读入数据。
|
||||
|
||||
<div align="center"> <img src="pics/lock-protocol-2.png"/> </div><br>
|
||||
<div align="center"> <img src="pics/lock-level-2.png" width="780px"/> </div><br>
|
||||
|
||||
**三级封锁协议**
|
||||
|
||||
@@ -197,7 +212,7 @@ MySQL 中提供了两种封锁粒度:行级锁以及表级锁。
|
||||
|
||||
可以解决不可重复读的问题,因为读 A 时,其它事务不能对 A 加 X 锁,从而避免了在读的期间数据发生改变。
|
||||
|
||||
<div align="center"> <img src="https://cs-notes-1256109796.cos.ap-guangzhou.myqcloud.com/image-20191207221313819.png"/> </div><br>
|
||||
<div align="center"> <img src="pics/lock-level-3.png" width="780px"/> </div><br>
|
||||
|
||||
#### 2. 两段锁协议
|
||||
|
||||
@@ -244,13 +259,22 @@ SELECT ... FOR UPDATE;
|
||||
|
||||
### 可串行化(SERIALIZABLE)
|
||||
|
||||
强制事务串行执行,这样多个事务互不干扰,不会出现并发一致性问题。
|
||||
保证并发执行的结果与某种串行执行的结果相同,不会出现并发一致性问题。
|
||||
|
||||
该隔离级别需要加锁实现,因为要使用加锁机制保证同一时间只有一个事务执行,也就是保证事务串行执行。
|
||||
InnoDB 的实现方式是在关闭自动提交时,把普通的 SELECT 都变成加共享锁的 SELECT ... FOR SHARE,读和写会互相阻塞,并发度最低。实测在可串行化的事务中读取 id 为 1 的行之后,另一个事务修改这一行会一直等待,直到锁等待超时;同样的操作在可重复读下可以立即完成。
|
||||
|
||||
----
|
||||
|
||||
<div align="center"> <img src="https://cs-notes-1256109796.cos.ap-guangzhou.myqcloud.com/image-20191207223400787.png"/> </div><br>
|
||||
各个隔离级别能否避免并发一致性问题:
|
||||
|
||||
| 隔离级别 | 脏读 | 不可重复读 | 幻影读 |
|
||||
| :---: | :---: | :---: | :---: |
|
||||
| 未提交读 | 可能 | 可能 | 可能 |
|
||||
| 提交读 | 不可能 | 可能 | 可能 |
|
||||
| 可重复读 | 不可能 | 不可能 | 可能 |
|
||||
| 可串行化 | 不可能 | 不可能 | 不可能 |
|
||||
|
||||
这是 SQL 标准的规定。MySQL 的 InnoDB 默认使用可重复读,并且用 MVCC 和 Next-Key Locks 在很大程度上避免了幻影读,见 [六、Next-Key Locks](#六next-key-locks);Oracle 和 PostgreSQL 默认使用提交读。
|
||||
|
||||
## 五、多版本并发控制
|
||||
|
||||
@@ -266,14 +290,13 @@ SELECT ... FOR UPDATE;
|
||||
|
||||
### 版本号
|
||||
|
||||
- 系统版本号 SYS_ID:是一个递增的数字,每开始一个新的事务,系统版本号就会自动递增。
|
||||
- 事务版本号 TRX_ID :事务开始时的系统版本号。
|
||||
InnoDB 为每个事务分配一个递增的事务 ID(TRX_ID),每个数据行都记录着最后修改它的事务 ID。需要注意的是,事务 ID 不是在 BEGIN 时分配的,而是在事务第一次修改数据时才分配;只读的事务不分配真正的事务 ID,也就不会出现在其它事务的 ReadView 中。在 MySQL 8.0 中实测:事务执行 SELECT 时 information_schema.innodb_trx 中显示的 trx_id 是一个很大的临时编号 281479674465408,执行 UPDATE 之后变为 3095。
|
||||
|
||||
### Undo 日志
|
||||
|
||||
MVCC 的多版本指的是多个版本的快照,快照存储在 Undo 日志中,该日志通过回滚指针 ROLL_PTR 把一个数据行的所有快照连接起来。
|
||||
|
||||
例如在 MySQL 创建一个表 t,包含主键 id 和一个字段 x。我们先插入一个数据行,然后对该数据行执行两次更新操作。
|
||||
例如在 MySQL 创建一个表 t,包含主键 id 和一个字段 x。先插入一个数据行,然后对该数据行执行两次更新操作。
|
||||
|
||||
```sql
|
||||
INSERT INTO t(id, x) VALUES(1, "a");
|
||||
@@ -281,9 +304,9 @@ UPDATE t SET x="b" WHERE id=1;
|
||||
UPDATE t SET x="c" WHERE id=1;
|
||||
```
|
||||
|
||||
因为没有使用 `START TRANSACTION` 将上面的操作当成一个事务来执行,根据 MySQL 的 AUTOCOMMIT 机制,每个操作都会被当成一个事务来执行,所以上面的操作总共涉及到三个事务。快照中除了记录事务版本号 TRX_ID 和操作之外,还记录了一个 bit 的 DEL 字段,用于标记是否被删除。
|
||||
因为没有使用 `START TRANSACTION` 将上面的操作当成一个事务来执行,根据 MySQL 的 AUTOCOMMIT 机制,每个操作都会被当成一个事务来执行,所以上面的操作总共涉及到三个事务,假设它们的 ID 依次为 101、102、103。快照中除了记录事务版本号 TRX_ID 和操作之外,还记录了一个 bit 的 DEL 字段,用于标记是否被删除。
|
||||
|
||||
<div align="center"> <img src="https://cs-notes-1256109796.cos.ap-guangzhou.myqcloud.com/image-20191208164808217.png"/> </div><br>
|
||||
<div align="center"> <img src="pics/mvcc-undo-chain.png" width="760px"/> </div><br>
|
||||
|
||||
INSERT、UPDATE、DELETE 操作会创建一个日志,并将事务版本号 TRX_ID 写入。DELETE 可以看成是一个特殊的 UPDATE,还会额外将 DEL 字段设置为 1。
|
||||
|
||||
@@ -291,7 +314,6 @@ INSERT、UPDATE、DELETE 操作会创建一个日志,并将事务版本号 TRX
|
||||
|
||||
MVCC 维护了一个 ReadView 结构,主要包含了创建 ReadView 时系统中未提交的事务列表 TRX_IDs {TRX_ID_1, TRX_ID_2, ...},该列表的最小值 TRX_ID_MIN,以及下一个将要分配的事务 ID TRX_ID_MAX。
|
||||
|
||||
<div align="center"> <img src="pics/readview.png"/> </div><br>
|
||||
|
||||
在进行 SELECT 操作时,根据数据行快照的 TRX_ID 与 TRX_ID_MIN 和 TRX_ID_MAX 之间的关系,从而判断数据行快照是否可以使用:
|
||||
|
||||
@@ -301,7 +323,13 @@ MVCC 维护了一个 ReadView 结构,主要包含了创建 ReadView 时系统
|
||||
|
||||
提交读和可重复读的判断规则相同,区别在于 ReadView 的创建时机:提交读在每次 SELECT 时都创建新的 ReadView,因此能读到其它事务新提交的修改;可重复读只在事务第一次 SELECT 时创建 ReadView,之后一直复用,因此多次读取的结果相同。
|
||||
|
||||
在数据行快照不可使用的情况下,需要沿着 Undo Log 的回滚指针 ROLL_PTR 找到下一个快照,再进行上面的判断。
|
||||
在数据行快照不可使用的情况下,需要沿着 Undo Log 的回滚指针 ROLL_PTR 找到下一个快照,再进行上面的判断。
|
||||
|
||||
接着上面的例子,假设事务 103 修改了 x 但还没有提交,另一个事务这时开始读取这一行:
|
||||
|
||||
<div align="center"> <img src="pics/mvcc-readview.png" width="760px"/> </div><br>
|
||||
|
||||
在 MySQL 8.0 上实际执行:事务 103 未提交时,可重复读的读事务读到 'b';103 提交之后,这个读事务再读仍然是 'b',而新开始的提交读事务读到 'c'。
|
||||
|
||||
### 快照读与当前读
|
||||
|
||||
@@ -323,11 +351,11 @@ UPDATE;
|
||||
DELETE;
|
||||
```
|
||||
|
||||
在进行 SELECT 操作时,可以强制指定进行加锁操作。以下第一个语句需要加 S 锁,第二个需要加 X 锁。
|
||||
在进行 SELECT 操作时,可以强制指定进行加锁操作。以下第一个语句需要加 S 锁,第二个需要加 X 锁。MySQL 8.0 起推荐用 FOR SHARE 代替 LOCK IN SHARE MODE。
|
||||
|
||||
```sql
|
||||
SELECT * FROM table WHERE ? lock in share mode;
|
||||
SELECT * FROM table WHERE ? for update;
|
||||
SELECT * FROM table WHERE ? FOR SHARE;
|
||||
SELECT * FROM table WHERE ? FOR UPDATE;
|
||||
```
|
||||
|
||||
## 六、Next-Key Locks
|
||||
@@ -336,6 +364,18 @@ Next-Key Locks 是 MySQL 的 InnoDB 存储引擎的一种锁实现。
|
||||
|
||||
在可重复读(REPEATABLE READ)隔离级别下,MVCC 使普通的 SELECT(快照读)不会读到其它事务新插入的行,但是 UPDATE、DELETE、SELECT ... FOR UPDATE 等当前读仍然可能出现幻读,Next-Key Locks 就是为了解决这个问题而存在的。InnoDB 使用 MVCC + Next-Key Locks 在可重复读隔离级别下解决了幻读问题。
|
||||
|
||||
需要注意的是,这要求一个事务中的读取全部是快照读或者全部是当前读,两者混用时仍然会出现幻读。在 MySQL 8.0 中实测,表 acct 原有 id 为 1、2 的两行:
|
||||
|
||||
```text
|
||||
A:SELECT * FROM acct WHERE balance >= 100; -- id 1、2
|
||||
B:INSERT INTO acct VALUES (3, 100); -- 自动提交
|
||||
A:SELECT * FROM acct WHERE balance >= 100; -- 仍然是 id 1、2
|
||||
A:UPDATE acct SET balance = balance + 10 WHERE balance >= 100; -- Rows matched: 3
|
||||
A:SELECT * FROM acct WHERE balance >= 100; -- id 1、2、3
|
||||
```
|
||||
|
||||
事务 A 的 UPDATE 是当前读,看到了事务 B 新插入并已提交的第 3 行,并且修改了它;这一行最新版本的事务 ID 变成了 A 自己,所以之后的快照读也能看到它。如果 A 一开始就用 SELECT ... FOR UPDATE 加锁读取,B 的插入会被 Next-Key Locks 阻塞,直到锁等待超时(ERROR 1205 Lock wait timeout exceeded)。
|
||||
|
||||
### Record Locks
|
||||
|
||||
锁定一个记录上的索引,而不是记录本身。
|
||||
@@ -362,7 +402,11 @@ SELECT c FROM t WHERE c BETWEEN 10 and 20 FOR UPDATE;
|
||||
(20, +∞)
|
||||
```
|
||||
|
||||
<div align="center"> <img src="pics/next-key-locks.png" width="560px"/> </div><br>
|
||||
在 MySQL 8.0 中建表 t(id, c),c 上有普通索引,插入 c 为 10、11、13、20 的四行,然后在可重复读下执行两种加锁读,从 performance_schema.data_locks 中查看实际加的锁,并在另一个事务中尝试插入:
|
||||
|
||||
<div align="center"> <img src="pics/next-key-locks-v2.png" width="760px"/> </div><br>
|
||||
|
||||
按 c = 11 查询时,c 不是唯一索引,后面还可能有 c = 11 的记录,所以除了 (10, 11] 的 Next-Key Lock,还要锁住到下一条记录 13 之间的间隙;如果 c 是唯一索引,就只锁 c = 11 这一条记录。
|
||||
|
||||
## 七、关系数据库设计理论
|
||||
|
||||
@@ -526,8 +570,11 @@ Entity-Relationship,有三个组成部分:实体、属性、联系。
|
||||
- 施瓦茨. 高性能 MYSQL(第3版)[M]. 电子工业出版社, 2013.
|
||||
- 史嘉权. 数据库系统概论[M]. 清华大学出版社有限公司, 2006.
|
||||
- [The InnoDB Storage Engine](https://dev.mysql.com/doc/refman/5.7/en/innodb-storage-engine.html)
|
||||
- [MySQL 8.0: Transaction Isolation Levels](https://dev.mysql.com/doc/refman/8.0/en/innodb-transaction-isolation-levels.html)
|
||||
- [MySQL 8.0: InnoDB Locking](https://dev.mysql.com/doc/refman/8.0/en/innodb-locking.html)
|
||||
- [MySQL 8.0: The data_locks Table](https://dev.mysql.com/doc/refman/8.0/en/performance-schema-data-locks-table.html)
|
||||
- [Transaction isolation levels](https://www.slideshare.net/ErnestoHernandezRodriguez/transaction-isolation-levels)
|
||||
- [Concurrency Control](http://scanftree.com/dbms/2-phase-locking-protocol)
|
||||
- [Concurrency Control](https://web.archive.org/web/20260614192237/https://scanftree.com/dbms/2-phase-locking-protocol)
|
||||
- [The Nightmare of Locking, Blocking and Isolation Levels!](https://www.slideshare.net/brshristov/the-nightmare-of-locking-blocking-and-isolation-levels-46391666)
|
||||
- [Database Normalization and Normal Forms with an Example](https://aksakalli.github.io/2012/03/12/database-normalization-and-normal-forms-with-an-example.html)
|
||||
- [The basics of the InnoDB undo logging and history system](https://blog.jcole.us/2014/04/16/the-basics-of-the-innodb-undo-logging-and-history-system/)
|
||||
|
||||
@@ -0,0 +1,58 @@
|
||||
<svg xmlns="http://www.w3.org/2000/svg" width="760" height="300" viewBox="0 0 760 300">
|
||||
<style>
|
||||
text { font-family: "PingFang SC", "Microsoft YaHei", Helvetica, Arial, sans-serif; fill: #3F3F3F; font-size: 12px; }
|
||||
.title { font-weight: bold; font-size: 15px; fill: #333; text-anchor: middle; }
|
||||
.b { font-weight: bold; }
|
||||
.c { text-anchor: middle; }
|
||||
.e { text-anchor: end; }
|
||||
.sub { font-size: 11px; fill: #6E6E6E; }
|
||||
.note { fill: #C0605A; }
|
||||
.ok { fill: #6A9F68; }
|
||||
.mono { font-family: Menlo, Consolas, monospace; }
|
||||
.arrow { stroke: #555; stroke-width: 1.1; fill: none; marker-end: url(#a); }
|
||||
.red { stroke: #C0605A; marker-end: url(#r); }
|
||||
.green { stroke: #6A9F68; marker-end: url(#g); }
|
||||
.dash { stroke-dasharray: 5 4; }
|
||||
.line { stroke: #555; stroke-width: 1.1; fill: none; }
|
||||
.frame { fill: none; stroke: #999; stroke-width: 1; stroke-dasharray: 4 3; }
|
||||
.shadow { fill: #DADADA; }
|
||||
</style>
|
||||
<defs>
|
||||
<marker id="a" viewBox="0 0 10 10" refX="9" refY="5" markerWidth="7" markerHeight="7" orient="auto-start-reverse">
|
||||
<path d="M1,1.5 L9.5,5 L1,8.5 z" fill="#555"/>
|
||||
</marker>
|
||||
<marker id="r" viewBox="0 0 10 10" refX="9" refY="5" markerWidth="7" markerHeight="7" orient="auto-start-reverse">
|
||||
<path d="M1,1.5 L9.5,5 L1,8.5 z" fill="#C0605A"/>
|
||||
</marker>
|
||||
<marker id="g" viewBox="0 0 10 10" refX="9" refY="5" markerWidth="7" markerHeight="7" orient="auto-start-reverse">
|
||||
<path d="M1,1.5 L9.5,5 L1,8.5 z" fill="#6A9F68"/>
|
||||
</marker>
|
||||
</defs>
|
||||
<rect width="100%" height="100%" fill="#FFFFFF"/>
|
||||
|
||||
<text x="380" y="28" class="title">ACID:原子性、隔离性、持久性是手段,一致性是目的</text>
|
||||
<rect x="40" y="48" width="170" height="46" rx="6" fill="#C6E4FB" stroke="#7FB2D6"/>
|
||||
<text x="125" y="69" class="b c">原子性</text>
|
||||
<text x="125" y="84" class="sub c">undo log 回滚</text>
|
||||
<rect x="40" y="128" width="170" height="46" rx="6" fill="#C6E4FB" stroke="#7FB2D6"/>
|
||||
<text x="125" y="149" class="b c">隔离性</text>
|
||||
<text x="125" y="164" class="sub c">锁和 MVCC</text>
|
||||
<rect x="40" y="208" width="170" height="46" rx="6" fill="#C6E4FB" stroke="#7FB2D6"/>
|
||||
<text x="125" y="229" class="b c">持久性</text>
|
||||
<text x="125" y="244" class="sub c">redo log 刷盘</text>
|
||||
<rect x="330" y="98" width="170" height="56" rx="6" fill="#E6E3FA" stroke="#B8B0DE"/>
|
||||
<text x="415" y="124" class="b c">一致性</text>
|
||||
<text x="415" y="139" class="sub c">满足约束和业务规则</text>
|
||||
<line x1="212" y1="70" x2="328" y2="116" class="arrow "/>
|
||||
<line x1="212" y1="150" x2="328" y2="132" class="arrow "/>
|
||||
<text x="270" y="82" class="c sub">没有并发时</text>
|
||||
<text x="270" y="166" class="c sub">有并发时还需要</text>
|
||||
<rect x="560" y="103" width="170" height="46" rx="6" fill="#C6ECE9" stroke="#7DBCB7"/>
|
||||
<text x="645" y="130.5" class="c sub b">事务的结果正确</text>
|
||||
<line x1="502" y1="126" x2="558" y2="126" class="arrow "/>
|
||||
<rect x="330" y="208" width="170" height="46" rx="6" fill="#EAE9E7" stroke="#BFBEBA"/>
|
||||
<text x="415" y="229" class="b c">系统崩溃后</text>
|
||||
<text x="415" y="244" class="sub c">已提交的修改不丢失</text>
|
||||
<line x1="212" y1="230" x2="328" y2="230" class="arrow "/>
|
||||
<text x="40" y="284" class="sub">例如转账:原子性保证不会只扣款不入账,隔离性保证并发的转账互不干扰,持久性保证提交之后断电也不丢。</text>
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 3.4 KiB |
@@ -0,0 +1,61 @@
|
||||
<svg xmlns="http://www.w3.org/2000/svg" width="780" height="440" viewBox="0 0 780 440">
|
||||
<style>
|
||||
text { font-family: "PingFang SC", "Microsoft YaHei", Helvetica, Arial, sans-serif; fill: #3F3F3F; font-size: 12px; }
|
||||
.title { font-weight: bold; font-size: 15px; fill: #333; text-anchor: middle; }
|
||||
.b { font-weight: bold; }
|
||||
.c { text-anchor: middle; }
|
||||
.e { text-anchor: end; }
|
||||
.sub { font-size: 11px; fill: #6E6E6E; }
|
||||
.note { fill: #C0605A; }
|
||||
.ok { fill: #6A9F68; }
|
||||
.mono { font-family: Menlo, Consolas, monospace; }
|
||||
.arrow { stroke: #555; stroke-width: 1.1; fill: none; marker-end: url(#a); }
|
||||
.red { stroke: #C0605A; marker-end: url(#r); }
|
||||
.green { stroke: #6A9F68; marker-end: url(#g); }
|
||||
.dash { stroke-dasharray: 5 4; }
|
||||
.line { stroke: #555; stroke-width: 1.1; fill: none; }
|
||||
.frame { fill: none; stroke: #999; stroke-width: 1; stroke-dasharray: 4 3; }
|
||||
.shadow { fill: #DADADA; }
|
||||
</style>
|
||||
<defs>
|
||||
<marker id="a" viewBox="0 0 10 10" refX="9" refY="5" markerWidth="7" markerHeight="7" orient="auto-start-reverse">
|
||||
<path d="M1,1.5 L9.5,5 L1,8.5 z" fill="#555"/>
|
||||
</marker>
|
||||
<marker id="r" viewBox="0 0 10 10" refX="9" refY="5" markerWidth="7" markerHeight="7" orient="auto-start-reverse">
|
||||
<path d="M1,1.5 L9.5,5 L1,8.5 z" fill="#C0605A"/>
|
||||
</marker>
|
||||
<marker id="g" viewBox="0 0 10 10" refX="9" refY="5" markerWidth="7" markerHeight="7" orient="auto-start-reverse">
|
||||
<path d="M1,1.5 L9.5,5 L1,8.5 z" fill="#6A9F68"/>
|
||||
</marker>
|
||||
</defs>
|
||||
<rect width="100%" height="100%" fill="#FFFFFF"/>
|
||||
|
||||
<text x="390" y="28" class="title">一级封锁协议:修改前加 X 锁,事务结束才释放</text>
|
||||
<rect x="40" y="50" width="335" height="34" rx="6" fill="#C6E4FB" stroke="#7FB2D6"/>
|
||||
<text x="207.5" y="71.5" class="c sub b">事务 T1</text>
|
||||
<line x1="207.5" y1="84" x2="207.5" y2="388" stroke="#D2D2D2" stroke-dasharray="4 3"/>
|
||||
<rect x="405" y="50" width="335" height="34" rx="6" fill="#C6ECE9" stroke="#7DBCB7"/>
|
||||
<text x="572.5" y="71.5" class="c sub b">事务 T2</text>
|
||||
<line x1="572.5" y1="84" x2="572.5" y2="388" stroke="#D2D2D2" stroke-dasharray="4 3"/>
|
||||
<text x="22" y="104" class="c sub">时间</text>
|
||||
<line x1="22" y1="110" x2="22" y2="382" class="arrow"/>
|
||||
<rect x="46" y="96" width="323" height="26" rx="5" fill="#FFFFFF" stroke="#7FB2D6"/>
|
||||
<text x="56" y="113" class="sub mono" style="font-size:10px;fill:#3F3F3F">lock-x(A)</text>
|
||||
<rect x="46" y="132" width="323" height="26" rx="5" fill="#FFFFFF" stroke="#7FB2D6"/>
|
||||
<text x="56" y="149" class="sub mono" style="font-size:10px;fill:#3F3F3F">read A</text>
|
||||
<text x="359" y="149" class="e sub mono" style="font-size:10px">→ 100</text>
|
||||
<rect x="411" y="168" width="323" height="26" rx="5" fill="#F6F6F6" stroke="#7DBCB7" stroke-dasharray="4 3"/>
|
||||
<text x="421" y="185" class="sub mono note" style="font-size:10px">lock-x(A),等待 T1 释放</text>
|
||||
<rect x="46" y="204" width="323" height="26" rx="5" fill="#FFFFFF" stroke="#7FB2D6"/>
|
||||
<text x="56" y="221" class="sub mono" style="font-size:10px;fill:#3F3F3F">write A = 70</text>
|
||||
<rect x="46" y="240" width="323" height="26" rx="5" fill="#FFFFFF" stroke="#7FB2D6"/>
|
||||
<text x="56" y="257" class="sub mono" style="font-size:10px;fill:#3F3F3F">commit,unlock(A)</text>
|
||||
<rect x="411" y="276" width="323" height="26" rx="5" fill="#FFFFFF" stroke="#7DBCB7"/>
|
||||
<text x="421" y="293" class="sub mono" style="font-size:10px;fill:#3F3F3F">获得 X 锁,read A</text>
|
||||
<text x="724" y="293" class="e sub mono ok b" style="font-size:10px">→ 70</text>
|
||||
<rect x="411" y="312" width="323" height="26" rx="5" fill="#FFFFFF" stroke="#7DBCB7"/>
|
||||
<text x="421" y="329" class="sub mono" style="font-size:10px;fill:#3F3F3F">write A = 120</text>
|
||||
<rect x="411" y="348" width="323" height="26" rx="5" fill="#FFFFFF" stroke="#7DBCB7"/>
|
||||
<text x="421" y="365" class="sub mono" style="font-size:10px;fill:#3F3F3F">commit,unlock(A)</text>
|
||||
<text x="30" y="408" class="sub b" style="fill:#3F3F3F">解决丢失修改:T2 只能在 T1 提交之后读取并修改,结果为 120。</text>
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 4.0 KiB |
@@ -0,0 +1,56 @@
|
||||
<svg xmlns="http://www.w3.org/2000/svg" width="780" height="368" viewBox="0 0 780 368">
|
||||
<style>
|
||||
text { font-family: "PingFang SC", "Microsoft YaHei", Helvetica, Arial, sans-serif; fill: #3F3F3F; font-size: 12px; }
|
||||
.title { font-weight: bold; font-size: 15px; fill: #333; text-anchor: middle; }
|
||||
.b { font-weight: bold; }
|
||||
.c { text-anchor: middle; }
|
||||
.e { text-anchor: end; }
|
||||
.sub { font-size: 11px; fill: #6E6E6E; }
|
||||
.note { fill: #C0605A; }
|
||||
.ok { fill: #6A9F68; }
|
||||
.mono { font-family: Menlo, Consolas, monospace; }
|
||||
.arrow { stroke: #555; stroke-width: 1.1; fill: none; marker-end: url(#a); }
|
||||
.red { stroke: #C0605A; marker-end: url(#r); }
|
||||
.green { stroke: #6A9F68; marker-end: url(#g); }
|
||||
.dash { stroke-dasharray: 5 4; }
|
||||
.line { stroke: #555; stroke-width: 1.1; fill: none; }
|
||||
.frame { fill: none; stroke: #999; stroke-width: 1; stroke-dasharray: 4 3; }
|
||||
.shadow { fill: #DADADA; }
|
||||
</style>
|
||||
<defs>
|
||||
<marker id="a" viewBox="0 0 10 10" refX="9" refY="5" markerWidth="7" markerHeight="7" orient="auto-start-reverse">
|
||||
<path d="M1,1.5 L9.5,5 L1,8.5 z" fill="#555"/>
|
||||
</marker>
|
||||
<marker id="r" viewBox="0 0 10 10" refX="9" refY="5" markerWidth="7" markerHeight="7" orient="auto-start-reverse">
|
||||
<path d="M1,1.5 L9.5,5 L1,8.5 z" fill="#C0605A"/>
|
||||
</marker>
|
||||
<marker id="g" viewBox="0 0 10 10" refX="9" refY="5" markerWidth="7" markerHeight="7" orient="auto-start-reverse">
|
||||
<path d="M1,1.5 L9.5,5 L1,8.5 z" fill="#6A9F68"/>
|
||||
</marker>
|
||||
</defs>
|
||||
<rect width="100%" height="100%" fill="#FFFFFF"/>
|
||||
|
||||
<text x="390" y="28" class="title">二级封锁协议:读取前加 S 锁,读完立即释放</text>
|
||||
<rect x="40" y="50" width="335" height="34" rx="6" fill="#C6E4FB" stroke="#7FB2D6"/>
|
||||
<text x="207.5" y="71.5" class="c sub b">事务 T1</text>
|
||||
<line x1="207.5" y1="84" x2="207.5" y2="316" stroke="#D2D2D2" stroke-dasharray="4 3"/>
|
||||
<rect x="405" y="50" width="335" height="34" rx="6" fill="#C6ECE9" stroke="#7DBCB7"/>
|
||||
<text x="572.5" y="71.5" class="c sub b">事务 T2</text>
|
||||
<line x1="572.5" y1="84" x2="572.5" y2="316" stroke="#D2D2D2" stroke-dasharray="4 3"/>
|
||||
<text x="22" y="104" class="c sub">时间</text>
|
||||
<line x1="22" y1="110" x2="22" y2="310" class="arrow"/>
|
||||
<rect x="46" y="96" width="323" height="26" rx="5" fill="#FFFFFF" stroke="#7FB2D6"/>
|
||||
<text x="56" y="113" class="sub mono" style="font-size:10px;fill:#3F3F3F">lock-x(A)</text>
|
||||
<rect x="46" y="132" width="323" height="26" rx="5" fill="#FFFFFF" stroke="#7FB2D6"/>
|
||||
<text x="56" y="149" class="sub mono" style="font-size:10px;fill:#3F3F3F">write A = 200</text>
|
||||
<rect x="411" y="168" width="323" height="26" rx="5" fill="#F6F6F6" stroke="#7DBCB7" stroke-dasharray="4 3"/>
|
||||
<text x="421" y="185" class="sub mono note" style="font-size:10px">lock-s(A),等待 T1 释放</text>
|
||||
<rect x="46" y="204" width="323" height="26" rx="5" fill="#FFFFFF" stroke="#7FB2D6"/>
|
||||
<text x="56" y="221" class="sub mono" style="font-size:10px;fill:#3F3F3F">rollback,unlock(A)</text>
|
||||
<rect x="411" y="240" width="323" height="26" rx="5" fill="#FFFFFF" stroke="#7DBCB7"/>
|
||||
<text x="421" y="257" class="sub mono" style="font-size:10px;fill:#3F3F3F">获得 S 锁,read A</text>
|
||||
<text x="724" y="257" class="e sub mono ok b" style="font-size:10px">→ 100</text>
|
||||
<rect x="411" y="276" width="323" height="26" rx="5" fill="#FFFFFF" stroke="#7DBCB7"/>
|
||||
<text x="421" y="293" class="sub mono" style="font-size:10px;fill:#3F3F3F">unlock(A)</text>
|
||||
<text x="30" y="336" class="sub b" style="fill:#3F3F3F">解决读脏数据:T1 持有 X 锁期间,T2 加不上 S 锁,读不到 T1 未提交的 200。</text>
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 3.6 KiB |
@@ -0,0 +1,59 @@
|
||||
<svg xmlns="http://www.w3.org/2000/svg" width="780" height="404" viewBox="0 0 780 404">
|
||||
<style>
|
||||
text { font-family: "PingFang SC", "Microsoft YaHei", Helvetica, Arial, sans-serif; fill: #3F3F3F; font-size: 12px; }
|
||||
.title { font-weight: bold; font-size: 15px; fill: #333; text-anchor: middle; }
|
||||
.b { font-weight: bold; }
|
||||
.c { text-anchor: middle; }
|
||||
.e { text-anchor: end; }
|
||||
.sub { font-size: 11px; fill: #6E6E6E; }
|
||||
.note { fill: #C0605A; }
|
||||
.ok { fill: #6A9F68; }
|
||||
.mono { font-family: Menlo, Consolas, monospace; }
|
||||
.arrow { stroke: #555; stroke-width: 1.1; fill: none; marker-end: url(#a); }
|
||||
.red { stroke: #C0605A; marker-end: url(#r); }
|
||||
.green { stroke: #6A9F68; marker-end: url(#g); }
|
||||
.dash { stroke-dasharray: 5 4; }
|
||||
.line { stroke: #555; stroke-width: 1.1; fill: none; }
|
||||
.frame { fill: none; stroke: #999; stroke-width: 1; stroke-dasharray: 4 3; }
|
||||
.shadow { fill: #DADADA; }
|
||||
</style>
|
||||
<defs>
|
||||
<marker id="a" viewBox="0 0 10 10" refX="9" refY="5" markerWidth="7" markerHeight="7" orient="auto-start-reverse">
|
||||
<path d="M1,1.5 L9.5,5 L1,8.5 z" fill="#555"/>
|
||||
</marker>
|
||||
<marker id="r" viewBox="0 0 10 10" refX="9" refY="5" markerWidth="7" markerHeight="7" orient="auto-start-reverse">
|
||||
<path d="M1,1.5 L9.5,5 L1,8.5 z" fill="#C0605A"/>
|
||||
</marker>
|
||||
<marker id="g" viewBox="0 0 10 10" refX="9" refY="5" markerWidth="7" markerHeight="7" orient="auto-start-reverse">
|
||||
<path d="M1,1.5 L9.5,5 L1,8.5 z" fill="#6A9F68"/>
|
||||
</marker>
|
||||
</defs>
|
||||
<rect width="100%" height="100%" fill="#FFFFFF"/>
|
||||
|
||||
<text x="390" y="28" class="title">三级封锁协议:读取前加 S 锁,事务结束才释放</text>
|
||||
<rect x="40" y="50" width="335" height="34" rx="6" fill="#C6E4FB" stroke="#7FB2D6"/>
|
||||
<text x="207.5" y="71.5" class="c sub b">事务 T1</text>
|
||||
<line x1="207.5" y1="84" x2="207.5" y2="352" stroke="#D2D2D2" stroke-dasharray="4 3"/>
|
||||
<rect x="405" y="50" width="335" height="34" rx="6" fill="#C6ECE9" stroke="#7DBCB7"/>
|
||||
<text x="572.5" y="71.5" class="c sub b">事务 T2</text>
|
||||
<line x1="572.5" y1="84" x2="572.5" y2="352" stroke="#D2D2D2" stroke-dasharray="4 3"/>
|
||||
<text x="22" y="104" class="c sub">时间</text>
|
||||
<line x1="22" y1="110" x2="22" y2="346" class="arrow"/>
|
||||
<rect x="411" y="96" width="323" height="26" rx="5" fill="#FFFFFF" stroke="#7DBCB7"/>
|
||||
<text x="421" y="113" class="sub mono" style="font-size:10px;fill:#3F3F3F">lock-s(A)</text>
|
||||
<rect x="411" y="132" width="323" height="26" rx="5" fill="#FFFFFF" stroke="#7DBCB7"/>
|
||||
<text x="421" y="149" class="sub mono" style="font-size:10px;fill:#3F3F3F">read A</text>
|
||||
<text x="724" y="149" class="e sub mono" style="font-size:10px">→ 100</text>
|
||||
<rect x="46" y="168" width="323" height="26" rx="5" fill="#F6F6F6" stroke="#7FB2D6" stroke-dasharray="4 3"/>
|
||||
<text x="56" y="185" class="sub mono note" style="font-size:10px">lock-x(A),等待 T2 释放</text>
|
||||
<rect x="411" y="204" width="323" height="26" rx="5" fill="#FFFFFF" stroke="#7DBCB7"/>
|
||||
<text x="421" y="221" class="sub mono" style="font-size:10px;fill:#3F3F3F">read A</text>
|
||||
<text x="724" y="221" class="e sub mono ok b" style="font-size:10px">→ 100</text>
|
||||
<rect x="411" y="240" width="323" height="26" rx="5" fill="#FFFFFF" stroke="#7DBCB7"/>
|
||||
<text x="421" y="257" class="sub mono" style="font-size:10px;fill:#3F3F3F">commit,unlock(A)</text>
|
||||
<rect x="46" y="276" width="323" height="26" rx="5" fill="#FFFFFF" stroke="#7FB2D6"/>
|
||||
<text x="56" y="293" class="sub mono" style="font-size:10px;fill:#3F3F3F">获得 X 锁,write A = 200</text>
|
||||
<rect x="46" y="312" width="323" height="26" rx="5" fill="#FFFFFF" stroke="#7FB2D6"/>
|
||||
<text x="56" y="329" class="sub mono" style="font-size:10px;fill:#3F3F3F">commit,unlock(A)</text>
|
||||
<text x="30" y="372" class="sub b" style="fill:#3F3F3F">解决不可重复读:T2 结束之前,T1 无法修改 A,T2 两次读到的都是 100。</text>
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 3.9 KiB |
@@ -0,0 +1,76 @@
|
||||
<svg xmlns="http://www.w3.org/2000/svg" width="760" height="400" viewBox="0 0 760 400">
|
||||
<style>
|
||||
text { font-family: "PingFang SC", "Microsoft YaHei", Helvetica, Arial, sans-serif; fill: #3F3F3F; font-size: 12px; }
|
||||
.title { font-weight: bold; font-size: 15px; fill: #333; text-anchor: middle; }
|
||||
.b { font-weight: bold; }
|
||||
.c { text-anchor: middle; }
|
||||
.e { text-anchor: end; }
|
||||
.sub { font-size: 11px; fill: #6E6E6E; }
|
||||
.note { fill: #C0605A; }
|
||||
.ok { fill: #6A9F68; }
|
||||
.mono { font-family: Menlo, Consolas, monospace; }
|
||||
.arrow { stroke: #555; stroke-width: 1.1; fill: none; marker-end: url(#a); }
|
||||
.red { stroke: #C0605A; marker-end: url(#r); }
|
||||
.green { stroke: #6A9F68; marker-end: url(#g); }
|
||||
.dash { stroke-dasharray: 5 4; }
|
||||
.line { stroke: #555; stroke-width: 1.1; fill: none; }
|
||||
.frame { fill: none; stroke: #999; stroke-width: 1; stroke-dasharray: 4 3; }
|
||||
.shadow { fill: #DADADA; }
|
||||
</style>
|
||||
<defs>
|
||||
<marker id="a" viewBox="0 0 10 10" refX="9" refY="5" markerWidth="7" markerHeight="7" orient="auto-start-reverse">
|
||||
<path d="M1,1.5 L9.5,5 L1,8.5 z" fill="#555"/>
|
||||
</marker>
|
||||
<marker id="r" viewBox="0 0 10 10" refX="9" refY="5" markerWidth="7" markerHeight="7" orient="auto-start-reverse">
|
||||
<path d="M1,1.5 L9.5,5 L1,8.5 z" fill="#C0605A"/>
|
||||
</marker>
|
||||
<marker id="g" viewBox="0 0 10 10" refX="9" refY="5" markerWidth="7" markerHeight="7" orient="auto-start-reverse">
|
||||
<path d="M1,1.5 L9.5,5 L1,8.5 z" fill="#6A9F68"/>
|
||||
</marker>
|
||||
</defs>
|
||||
<rect width="100%" height="100%" fill="#FFFFFF"/>
|
||||
|
||||
<text x="380" y="28" class="title">ReadView:沿版本链找到第一个对自己可见的版本</text>
|
||||
<text x="30" y="64" class="sub">读事务开始读取时,事务 103、105 还没有提交,下一个要分配的事务 ID 是 106:</text>
|
||||
<text x="30" y="82" class="sub mono" style="fill:#3F3F3F">TRX_IDs = {103, 105},TRX_ID_MIN = 103,TRX_ID_MAX = 106</text>
|
||||
<line x1="50" y1="140" x2="650" y2="140" class="arrow"/>
|
||||
<text x="650" y="128" class="e sub">事务 ID</text>
|
||||
<circle cx="90" cy="140" r="15" fill="#C6ECE9" stroke="#7DBCB7"/>
|
||||
<text x="90" y="144" class="c sub mono b" style="fill:#3F3F3F">101</text>
|
||||
<text x="90" y="176" class="c sub b ok">可见</text>
|
||||
<text x="90" y="194" class="c sub">已提交</text>
|
||||
<circle cx="190" cy="140" r="15" fill="#C6ECE9" stroke="#7DBCB7"/>
|
||||
<text x="190" y="144" class="c sub mono b" style="fill:#3F3F3F">102</text>
|
||||
<text x="190" y="176" class="c sub b ok">可见</text>
|
||||
<text x="190" y="194" class="c sub">已提交</text>
|
||||
<circle cx="290" cy="140" r="15" fill="#F3C6C2" stroke="#D99A94"/>
|
||||
<text x="290" y="144" class="c sub mono b" style="fill:#3F3F3F">103</text>
|
||||
<text x="290" y="176" class="c sub b note">不可见</text>
|
||||
<text x="290" y="194" class="c sub">在 TRX_IDs 中</text>
|
||||
<circle cx="390" cy="140" r="15" fill="#C6ECE9" stroke="#7DBCB7"/>
|
||||
<text x="390" y="144" class="c sub mono b" style="fill:#3F3F3F">104</text>
|
||||
<text x="390" y="176" class="c sub b ok">可见</text>
|
||||
<text x="390" y="194" class="c sub">已提交</text>
|
||||
<circle cx="490" cy="140" r="15" fill="#F3C6C2" stroke="#D99A94"/>
|
||||
<text x="490" y="144" class="c sub mono b" style="fill:#3F3F3F">105</text>
|
||||
<text x="490" y="176" class="c sub b note">不可见</text>
|
||||
<text x="490" y="194" class="c sub">在 TRX_IDs 中</text>
|
||||
<circle cx="590" cy="140" r="15" fill="#F3C6C2" stroke="#D99A94"/>
|
||||
<text x="590" y="144" class="c sub mono b" style="fill:#3F3F3F">106</text>
|
||||
<text x="590" y="176" class="c sub b note">不可见</text>
|
||||
<text x="590" y="194" class="c sub">之后才开始</text>
|
||||
<text x="290" y="112" class="c sub mono" style="fill:#766AA8">TRX_ID_MIN</text>
|
||||
<text x="590" y="112" class="c sub mono" style="fill:#766AA8">TRX_ID_MAX</text>
|
||||
<text x="30" y="236" class="sub b">读取 id = 1 这一行:</text>
|
||||
<rect x="30" y="250" width="150" height="34" rx="6" fill="#C6E4FB" stroke="#7FB2D6"/>
|
||||
<text x="105" y="271.5" class="c sub b">版本 x = 'c'</text>
|
||||
<text x="196" y="265" class="sub mono">DB_TRX_ID = 103,在 TRX_IDs 中</text>
|
||||
<text x="196" y="281" class="sub b note">不可见,沿回滚指针找上一个版本</text>
|
||||
<line x1="105" y1="284" x2="105" y2="300" class="arrow "/>
|
||||
<rect x="30" y="300" width="150" height="34" rx="6" fill="#C6ECE9" stroke="#7DBCB7"/>
|
||||
<text x="105" y="321.5" class="c sub b">版本 x = 'b'</text>
|
||||
<text x="196" y="315" class="sub mono">DB_TRX_ID = 102,小于 TRX_ID_MIN</text>
|
||||
<text x="196" y="331" class="sub b ok">可见,读到 'b'</text>
|
||||
<text x="30" y="374" class="sub">可重复读在第一次读取时创建 ReadView,之后一直使用它;提交读每次读取都创建新的 ReadView,103 提交后就能读到 'c'。</text>
|
||||
<text x="30" y="392" class="sub">事务 ID 为示意;读到的结果在 MySQL 8.0 上实测一致。</text>
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 4.8 KiB |
@@ -0,0 +1,91 @@
|
||||
<svg xmlns="http://www.w3.org/2000/svg" width="760" height="330" viewBox="0 0 760 330">
|
||||
<style>
|
||||
text { font-family: "PingFang SC", "Microsoft YaHei", Helvetica, Arial, sans-serif; fill: #3F3F3F; font-size: 12px; }
|
||||
.title { font-weight: bold; font-size: 15px; fill: #333; text-anchor: middle; }
|
||||
.b { font-weight: bold; }
|
||||
.c { text-anchor: middle; }
|
||||
.e { text-anchor: end; }
|
||||
.sub { font-size: 11px; fill: #6E6E6E; }
|
||||
.note { fill: #C0605A; }
|
||||
.ok { fill: #6A9F68; }
|
||||
.mono { font-family: Menlo, Consolas, monospace; }
|
||||
.arrow { stroke: #555; stroke-width: 1.1; fill: none; marker-end: url(#a); }
|
||||
.red { stroke: #C0605A; marker-end: url(#r); }
|
||||
.green { stroke: #6A9F68; marker-end: url(#g); }
|
||||
.dash { stroke-dasharray: 5 4; }
|
||||
.line { stroke: #555; stroke-width: 1.1; fill: none; }
|
||||
.frame { fill: none; stroke: #999; stroke-width: 1; stroke-dasharray: 4 3; }
|
||||
.shadow { fill: #DADADA; }
|
||||
</style>
|
||||
<defs>
|
||||
<marker id="a" viewBox="0 0 10 10" refX="9" refY="5" markerWidth="7" markerHeight="7" orient="auto-start-reverse">
|
||||
<path d="M1,1.5 L9.5,5 L1,8.5 z" fill="#555"/>
|
||||
</marker>
|
||||
<marker id="r" viewBox="0 0 10 10" refX="9" refY="5" markerWidth="7" markerHeight="7" orient="auto-start-reverse">
|
||||
<path d="M1,1.5 L9.5,5 L1,8.5 z" fill="#C0605A"/>
|
||||
</marker>
|
||||
<marker id="g" viewBox="0 0 10 10" refX="9" refY="5" markerWidth="7" markerHeight="7" orient="auto-start-reverse">
|
||||
<path d="M1,1.5 L9.5,5 L1,8.5 z" fill="#6A9F68"/>
|
||||
</marker>
|
||||
</defs>
|
||||
<rect width="100%" height="100%" fill="#FFFFFF"/>
|
||||
|
||||
<text x="380" y="28" class="title">Undo 版本链:每次修改都把旧版本写入 undo log,用回滚指针连起来</text>
|
||||
<text x="40" y="70" class="sub b">当前的数据行</text>
|
||||
<rect x="40" y="76" width="44" height="24" rx="3" fill="#EAE9E7" stroke="#BFBEBA" stroke-width="1"/>
|
||||
<text x="62" y="92" class="c sub b" style="fill:#444">id</text>
|
||||
<rect x="84" y="76" width="44" height="24" rx="3" fill="#EAE9E7" stroke="#BFBEBA" stroke-width="1"/>
|
||||
<text x="106" y="92" class="c sub b" style="fill:#444">x</text>
|
||||
<rect x="128" y="76" width="92" height="24" rx="3" fill="#EAE9E7" stroke="#BFBEBA" stroke-width="1"/>
|
||||
<text x="174" y="92" class="c sub b" style="fill:#444">DB_TRX_ID</text>
|
||||
<rect x="220" y="76" width="104" height="24" rx="3" fill="#EAE9E7" stroke="#BFBEBA" stroke-width="1"/>
|
||||
<text x="272" y="92" class="c sub b" style="fill:#444">DB_ROLL_PTR</text>
|
||||
<rect x="40" y="100" width="44" height="24" rx="3" fill="#FFFFFF" stroke="#BBBBBB" stroke-width="1"/>
|
||||
<text x="62" y="116" class="c sub mono" style="fill:#444">1</text>
|
||||
<rect x="84" y="100" width="44" height="24" rx="3" fill="#FFFFFF" stroke="#BBBBBB" stroke-width="1"/>
|
||||
<text x="106" y="116" class="c sub mono" style="fill:#444">c</text>
|
||||
<rect x="128" y="100" width="92" height="24" rx="3" fill="#FFFFFF" stroke="#BBBBBB" stroke-width="1"/>
|
||||
<text x="174" y="116" class="c sub mono" style="fill:#444">103</text>
|
||||
<rect x="220" y="100" width="104" height="24" rx="3" fill="#FFFFFF" stroke="#BBBBBB" stroke-width="1"/>
|
||||
<text x="272" y="116" class="c sub mono" style="fill:#444">→ 版本 2</text>
|
||||
<text x="350" y="106" class="sub mono">UPDATE t SET x = 'c'(事务 103)</text>
|
||||
<line x1="272" y1="125" x2="272" y2="152" class="arrow "/>
|
||||
<text x="40" y="148" class="sub b">undo log 中的版本 2</text>
|
||||
<rect x="40" y="154" width="44" height="24" rx="3" fill="#EAE9E7" stroke="#BFBEBA" stroke-width="1"/>
|
||||
<text x="62" y="170" class="c sub b" style="fill:#444">id</text>
|
||||
<rect x="84" y="154" width="44" height="24" rx="3" fill="#EAE9E7" stroke="#BFBEBA" stroke-width="1"/>
|
||||
<text x="106" y="170" class="c sub b" style="fill:#444">x</text>
|
||||
<rect x="128" y="154" width="92" height="24" rx="3" fill="#EAE9E7" stroke="#BFBEBA" stroke-width="1"/>
|
||||
<text x="174" y="170" class="c sub b" style="fill:#444">DB_TRX_ID</text>
|
||||
<rect x="220" y="154" width="104" height="24" rx="3" fill="#EAE9E7" stroke="#BFBEBA" stroke-width="1"/>
|
||||
<text x="272" y="170" class="c sub b" style="fill:#444">DB_ROLL_PTR</text>
|
||||
<rect x="40" y="178" width="44" height="24" rx="3" fill="#FFFFFF" stroke="#BBBBBB" stroke-width="1"/>
|
||||
<text x="62" y="194" class="c sub mono" style="fill:#444">1</text>
|
||||
<rect x="84" y="178" width="44" height="24" rx="3" fill="#FFFFFF" stroke="#BBBBBB" stroke-width="1"/>
|
||||
<text x="106" y="194" class="c sub mono" style="fill:#444">b</text>
|
||||
<rect x="128" y="178" width="92" height="24" rx="3" fill="#FFFFFF" stroke="#BBBBBB" stroke-width="1"/>
|
||||
<text x="174" y="194" class="c sub mono" style="fill:#444">102</text>
|
||||
<rect x="220" y="178" width="104" height="24" rx="3" fill="#FFFFFF" stroke="#BBBBBB" stroke-width="1"/>
|
||||
<text x="272" y="194" class="c sub mono" style="fill:#444">→ 版本 1</text>
|
||||
<text x="350" y="184" class="sub mono">UPDATE t SET x = 'b'(事务 102)</text>
|
||||
<line x1="272" y1="203" x2="272" y2="230" class="arrow "/>
|
||||
<text x="40" y="226" class="sub b">undo log 中的版本 1</text>
|
||||
<rect x="40" y="232" width="44" height="24" rx="3" fill="#EAE9E7" stroke="#BFBEBA" stroke-width="1"/>
|
||||
<text x="62" y="248" class="c sub b" style="fill:#444">id</text>
|
||||
<rect x="84" y="232" width="44" height="24" rx="3" fill="#EAE9E7" stroke="#BFBEBA" stroke-width="1"/>
|
||||
<text x="106" y="248" class="c sub b" style="fill:#444">x</text>
|
||||
<rect x="128" y="232" width="92" height="24" rx="3" fill="#EAE9E7" stroke="#BFBEBA" stroke-width="1"/>
|
||||
<text x="174" y="248" class="c sub b" style="fill:#444">DB_TRX_ID</text>
|
||||
<rect x="220" y="232" width="104" height="24" rx="3" fill="#EAE9E7" stroke="#BFBEBA" stroke-width="1"/>
|
||||
<text x="272" y="248" class="c sub b" style="fill:#444">DB_ROLL_PTR</text>
|
||||
<rect x="40" y="256" width="44" height="24" rx="3" fill="#FFFFFF" stroke="#BBBBBB" stroke-width="1"/>
|
||||
<text x="62" y="272" class="c sub mono" style="fill:#444">1</text>
|
||||
<rect x="84" y="256" width="44" height="24" rx="3" fill="#FFFFFF" stroke="#BBBBBB" stroke-width="1"/>
|
||||
<text x="106" y="272" class="c sub mono" style="fill:#444">a</text>
|
||||
<rect x="128" y="256" width="92" height="24" rx="3" fill="#FFFFFF" stroke="#BBBBBB" stroke-width="1"/>
|
||||
<text x="174" y="272" class="c sub mono" style="fill:#444">101</text>
|
||||
<rect x="220" y="256" width="104" height="24" rx="3" fill="#FFFFFF" stroke="#BBBBBB" stroke-width="1"/>
|
||||
<text x="272" y="272" class="c sub mono" style="fill:#444">无更早版本</text>
|
||||
<text x="350" y="262" class="sub mono">INSERT INTO t VALUES (1, 'a')(事务 101)</text>
|
||||
<text x="40" y="314" class="sub">DB_TRX_ID 是最后修改这一行的事务 ID,DB_ROLL_PTR 指向上一个版本;事务 ID 为示意。</text>
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 6.5 KiB |
@@ -0,0 +1,79 @@
|
||||
<svg xmlns="http://www.w3.org/2000/svg" width="760" height="330" viewBox="0 0 760 330">
|
||||
<style>
|
||||
text { font-family: "PingFang SC", "Microsoft YaHei", Helvetica, Arial, sans-serif; fill: #3F3F3F; font-size: 12px; }
|
||||
.title { font-weight: bold; font-size: 15px; fill: #333; text-anchor: middle; }
|
||||
.b { font-weight: bold; }
|
||||
.c { text-anchor: middle; }
|
||||
.e { text-anchor: end; }
|
||||
.sub { font-size: 11px; fill: #6E6E6E; }
|
||||
.note { fill: #C0605A; }
|
||||
.ok { fill: #6A9F68; }
|
||||
.mono { font-family: Menlo, Consolas, monospace; }
|
||||
.arrow { stroke: #555; stroke-width: 1.1; fill: none; marker-end: url(#a); }
|
||||
.red { stroke: #C0605A; marker-end: url(#r); }
|
||||
.green { stroke: #6A9F68; marker-end: url(#g); }
|
||||
.dash { stroke-dasharray: 5 4; }
|
||||
.line { stroke: #555; stroke-width: 1.1; fill: none; }
|
||||
.frame { fill: none; stroke: #999; stroke-width: 1; stroke-dasharray: 4 3; }
|
||||
.shadow { fill: #DADADA; }
|
||||
</style>
|
||||
<defs>
|
||||
<marker id="a" viewBox="0 0 10 10" refX="9" refY="5" markerWidth="7" markerHeight="7" orient="auto-start-reverse">
|
||||
<path d="M1,1.5 L9.5,5 L1,8.5 z" fill="#555"/>
|
||||
</marker>
|
||||
<marker id="r" viewBox="0 0 10 10" refX="9" refY="5" markerWidth="7" markerHeight="7" orient="auto-start-reverse">
|
||||
<path d="M1,1.5 L9.5,5 L1,8.5 z" fill="#C0605A"/>
|
||||
</marker>
|
||||
<marker id="g" viewBox="0 0 10 10" refX="9" refY="5" markerWidth="7" markerHeight="7" orient="auto-start-reverse">
|
||||
<path d="M1,1.5 L9.5,5 L1,8.5 z" fill="#6A9F68"/>
|
||||
</marker>
|
||||
</defs>
|
||||
<rect width="100%" height="100%" fill="#FFFFFF"/>
|
||||
|
||||
<text x="380" y="28" class="title">Next-Key Locks:锁住记录和它前面的间隙</text>
|
||||
<text x="30" y="70" class="sub mono b" style="fill:#3F3F3F">① SELECT * FROM t WHERE c = 11 FOR UPDATE</text>
|
||||
<rect x="194" y="94" width="102" height="14" rx="3" fill="#E6E3FA" stroke="#B8B0DE"/>
|
||||
<text x="245" y="105" class="c sub mono" style="font-size:10px;fill:#3F3F3F">(10, 11]</text>
|
||||
<rect x="304" y="94" width="112" height="14" rx="3" fill="#C6E4FB" stroke="#7FB2D6"/>
|
||||
<text x="360" y="105" class="c sub mono" style="font-size:10px;fill:#3F3F3F">(11, 13)</text>
|
||||
<line x1="60" y1="116" x2="720" y2="116" stroke="#999"/>
|
||||
<text x="60" y="134" class="c sub mono">-∞</text>
|
||||
<circle cx="190" cy="116" r="4" fill="#555"/>
|
||||
<text x="190" y="134" class="c sub mono">c=10</text>
|
||||
<circle cx="300" cy="116" r="4" fill="#555"/>
|
||||
<text x="300" y="134" class="c sub mono">c=11</text>
|
||||
<circle cx="420" cy="116" r="4" fill="#555"/>
|
||||
<text x="420" y="134" class="c sub mono">c=13</text>
|
||||
<circle cx="580" cy="116" r="4" fill="#555"/>
|
||||
<text x="580" y="134" class="c sub mono">c=20</text>
|
||||
<text x="720" y="134" class="c sub mono">+∞</text>
|
||||
<text x="30" y="156" class="sub">data_locks:c = 11 的记录上是 X(Next-Key Lock),c = 13 上是 X,GAP;插入 c = 12 被阻塞,c = 9、14、21 可以插入</text>
|
||||
<text x="30" y="200" class="sub mono b" style="fill:#3F3F3F">② SELECT * FROM t WHERE c BETWEEN 10 AND 20 FOR UPDATE</text>
|
||||
<rect x="64" y="224" width="122" height="14" rx="3" fill="#E6E3FA" stroke="#B8B0DE"/>
|
||||
<text x="125" y="235" class="c sub mono" style="font-size:10px;fill:#3F3F3F">(-∞, 10]</text>
|
||||
<rect x="194" y="224" width="102" height="14" rx="3" fill="#E6E3FA" stroke="#B8B0DE"/>
|
||||
<text x="245" y="235" class="c sub mono" style="font-size:10px;fill:#3F3F3F">(10, 11]</text>
|
||||
<rect x="304" y="224" width="112" height="14" rx="3" fill="#E6E3FA" stroke="#B8B0DE"/>
|
||||
<text x="360" y="235" class="c sub mono" style="font-size:10px;fill:#3F3F3F">(11, 13]</text>
|
||||
<rect x="424" y="224" width="152" height="14" rx="3" fill="#E6E3FA" stroke="#B8B0DE"/>
|
||||
<text x="500" y="235" class="c sub mono" style="font-size:10px;fill:#3F3F3F">(13, 20]</text>
|
||||
<rect x="584" y="224" width="132" height="14" rx="3" fill="#E6E3FA" stroke="#B8B0DE"/>
|
||||
<text x="650" y="235" class="c sub mono" style="font-size:10px;fill:#3F3F3F">(20, +∞]</text>
|
||||
<line x1="60" y1="246" x2="720" y2="246" stroke="#999"/>
|
||||
<text x="60" y="264" class="c sub mono">-∞</text>
|
||||
<circle cx="190" cy="246" r="4" fill="#555"/>
|
||||
<text x="190" y="264" class="c sub mono">c=10</text>
|
||||
<circle cx="300" cy="246" r="4" fill="#555"/>
|
||||
<text x="300" y="264" class="c sub mono">c=11</text>
|
||||
<circle cx="420" cy="246" r="4" fill="#555"/>
|
||||
<text x="420" y="264" class="c sub mono">c=13</text>
|
||||
<circle cx="580" cy="246" r="4" fill="#555"/>
|
||||
<text x="580" y="264" class="c sub mono">c=20</text>
|
||||
<text x="720" y="264" class="c sub mono">+∞</text>
|
||||
<text x="30" y="286" class="sub">data_locks:c = 10、11、13、20 和 supremum 上都是 X;插入 c = 9、12、14、21 都被阻塞</text>
|
||||
<rect x="30" y="302" width="18" height="12" rx="3" fill="#E6E3FA" stroke="#B8B0DE"/>
|
||||
<text x="54" y="312" class="sub">Next-Key Lock(前开后闭)</text>
|
||||
<rect x="230" y="302" width="18" height="12" rx="3" fill="#C6E4FB" stroke="#7FB2D6"/>
|
||||
<text x="254" y="312" class="sub">Gap Lock(开区间)</text>
|
||||
<text x="420" y="312" class="sub">MySQL 8.0,c 上有普通索引,可重复读</text>
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 5.0 KiB |
@@ -0,0 +1,57 @@
|
||||
<svg xmlns="http://www.w3.org/2000/svg" width="780" height="354" viewBox="0 0 780 354">
|
||||
<style>
|
||||
text { font-family: "PingFang SC", "Microsoft YaHei", Helvetica, Arial, sans-serif; fill: #3F3F3F; font-size: 12px; }
|
||||
.title { font-weight: bold; font-size: 15px; fill: #333; text-anchor: middle; }
|
||||
.b { font-weight: bold; }
|
||||
.c { text-anchor: middle; }
|
||||
.e { text-anchor: end; }
|
||||
.sub { font-size: 11px; fill: #6E6E6E; }
|
||||
.note { fill: #C0605A; }
|
||||
.ok { fill: #6A9F68; }
|
||||
.mono { font-family: Menlo, Consolas, monospace; }
|
||||
.arrow { stroke: #555; stroke-width: 1.1; fill: none; marker-end: url(#a); }
|
||||
.red { stroke: #C0605A; marker-end: url(#r); }
|
||||
.green { stroke: #6A9F68; marker-end: url(#g); }
|
||||
.dash { stroke-dasharray: 5 4; }
|
||||
.line { stroke: #555; stroke-width: 1.1; fill: none; }
|
||||
.frame { fill: none; stroke: #999; stroke-width: 1; stroke-dasharray: 4 3; }
|
||||
.shadow { fill: #DADADA; }
|
||||
</style>
|
||||
<defs>
|
||||
<marker id="a" viewBox="0 0 10 10" refX="9" refY="5" markerWidth="7" markerHeight="7" orient="auto-start-reverse">
|
||||
<path d="M1,1.5 L9.5,5 L1,8.5 z" fill="#555"/>
|
||||
</marker>
|
||||
<marker id="r" viewBox="0 0 10 10" refX="9" refY="5" markerWidth="7" markerHeight="7" orient="auto-start-reverse">
|
||||
<path d="M1,1.5 L9.5,5 L1,8.5 z" fill="#C0605A"/>
|
||||
</marker>
|
||||
<marker id="g" viewBox="0 0 10 10" refX="9" refY="5" markerWidth="7" markerHeight="7" orient="auto-start-reverse">
|
||||
<path d="M1,1.5 L9.5,5 L1,8.5 z" fill="#6A9F68"/>
|
||||
</marker>
|
||||
</defs>
|
||||
<rect width="100%" height="100%" fill="#FFFFFF"/>
|
||||
|
||||
<text x="390" y="28" class="title">读脏数据:读到了另一个事务没有提交的修改</text>
|
||||
<rect x="40" y="50" width="335" height="34" rx="6" fill="#C6E4FB" stroke="#7FB2D6"/>
|
||||
<text x="207.5" y="71.5" class="c sub b">事务 T1</text>
|
||||
<line x1="207.5" y1="84" x2="207.5" y2="280" stroke="#D2D2D2" stroke-dasharray="4 3"/>
|
||||
<rect x="405" y="50" width="335" height="34" rx="6" fill="#C6ECE9" stroke="#7DBCB7"/>
|
||||
<text x="572.5" y="65" class="c sub b">事务 T2</text>
|
||||
<text x="572.5" y="80" class="sub c">READ UNCOMMITTED</text>
|
||||
<line x1="572.5" y1="84" x2="572.5" y2="280" stroke="#D2D2D2" stroke-dasharray="4 3"/>
|
||||
<text x="22" y="104" class="c sub">时间</text>
|
||||
<line x1="22" y1="110" x2="22" y2="274" class="arrow"/>
|
||||
<rect x="46" y="96" width="323" height="26" rx="5" fill="#FFFFFF" stroke="#7FB2D6"/>
|
||||
<text x="56" y="113" class="sub mono" style="font-size:10px;fill:#3F3F3F">BEGIN</text>
|
||||
<rect x="46" y="132" width="323" height="26" rx="5" fill="#FFFFFF" stroke="#7FB2D6"/>
|
||||
<text x="56" y="149" class="sub mono" style="font-size:10px;fill:#3F3F3F">UPDATE account SET balance = 200 WHERE id = 1</text>
|
||||
<rect x="411" y="168" width="323" height="26" rx="5" fill="#FFFFFF" stroke="#7DBCB7"/>
|
||||
<text x="421" y="185" class="sub mono" style="font-size:10px;fill:#3F3F3F">SELECT balance FROM account WHERE id = 1</text>
|
||||
<text x="724" y="185" class="e sub mono note b" style="font-size:10px">→ 200</text>
|
||||
<rect x="46" y="204" width="323" height="26" rx="5" fill="#FFFFFF" stroke="#7FB2D6"/>
|
||||
<text x="56" y="221" class="sub mono" style="font-size:10px;fill:#3F3F3F">ROLLBACK</text>
|
||||
<rect x="411" y="240" width="323" height="26" rx="5" fill="#FFFFFF" stroke="#7DBCB7"/>
|
||||
<text x="421" y="257" class="sub mono" style="font-size:10px;fill:#3F3F3F">SELECT balance FROM account WHERE id = 1</text>
|
||||
<text x="724" y="257" class="e sub mono" style="font-size:10px">→ 100</text>
|
||||
<text x="30" y="300" class="sub b" style="fill:#3F3F3F">T2 读到的 200 从来没有提交过。</text>
|
||||
<text x="30" y="322" class="sub" style="">T2 的隔离级别为 READ COMMITTED 及以上时,第一次读到的是 100。</text>
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 3.7 KiB |
@@ -0,0 +1,62 @@
|
||||
<svg xmlns="http://www.w3.org/2000/svg" width="760" height="300" viewBox="0 0 760 300">
|
||||
<style>
|
||||
text { font-family: "PingFang SC", "Microsoft YaHei", Helvetica, Arial, sans-serif; fill: #3F3F3F; font-size: 12px; }
|
||||
.title { font-weight: bold; font-size: 15px; fill: #333; text-anchor: middle; }
|
||||
.b { font-weight: bold; }
|
||||
.c { text-anchor: middle; }
|
||||
.e { text-anchor: end; }
|
||||
.sub { font-size: 11px; fill: #6E6E6E; }
|
||||
.note { fill: #C0605A; }
|
||||
.ok { fill: #6A9F68; }
|
||||
.mono { font-family: Menlo, Consolas, monospace; }
|
||||
.arrow { stroke: #555; stroke-width: 1.1; fill: none; marker-end: url(#a); }
|
||||
.red { stroke: #C0605A; marker-end: url(#r); }
|
||||
.green { stroke: #6A9F68; marker-end: url(#g); }
|
||||
.dash { stroke-dasharray: 5 4; }
|
||||
.line { stroke: #555; stroke-width: 1.1; fill: none; }
|
||||
.frame { fill: none; stroke: #999; stroke-width: 1; stroke-dasharray: 4 3; }
|
||||
.shadow { fill: #DADADA; }
|
||||
</style>
|
||||
<defs>
|
||||
<marker id="a" viewBox="0 0 10 10" refX="9" refY="5" markerWidth="7" markerHeight="7" orient="auto-start-reverse">
|
||||
<path d="M1,1.5 L9.5,5 L1,8.5 z" fill="#555"/>
|
||||
</marker>
|
||||
<marker id="r" viewBox="0 0 10 10" refX="9" refY="5" markerWidth="7" markerHeight="7" orient="auto-start-reverse">
|
||||
<path d="M1,1.5 L9.5,5 L1,8.5 z" fill="#C0605A"/>
|
||||
</marker>
|
||||
<marker id="g" viewBox="0 0 10 10" refX="9" refY="5" markerWidth="7" markerHeight="7" orient="auto-start-reverse">
|
||||
<path d="M1,1.5 L9.5,5 L1,8.5 z" fill="#6A9F68"/>
|
||||
</marker>
|
||||
</defs>
|
||||
<rect width="100%" height="100%" fill="#FFFFFF"/>
|
||||
|
||||
<text x="380" y="28" class="title">事务:要么全部提交,要么全部回滚</text>
|
||||
<text x="30" y="64" class="sub">从 A 账户向 B 账户转账 30 元</text>
|
||||
<rect x="30" y="84" width="140" height="58" rx="6" fill="#EAE9E7" stroke="#BFBEBA"/>
|
||||
<text x="100" y="102" class="c sub b">一致的状态</text>
|
||||
<text x="100" y="120" class="c sub mono">A = 100</text>
|
||||
<text x="100" y="136" class="c sub mono">B = 0</text>
|
||||
<rect x="220" y="84" width="140" height="58" rx="6" fill="#E6E3FA" stroke="#B8B0DE"/>
|
||||
<text x="290" y="102" class="c sub b">中间状态</text>
|
||||
<text x="290" y="120" class="c sub mono">A = 70</text>
|
||||
<text x="290" y="136" class="c sub mono">B = 0</text>
|
||||
<rect x="410" y="84" width="140" height="58" rx="6" fill="#E6E3FA" stroke="#B8B0DE"/>
|
||||
<text x="480" y="102" class="c sub b">中间状态</text>
|
||||
<text x="480" y="120" class="c sub mono">A = 70</text>
|
||||
<text x="480" y="136" class="c sub mono">B = 30</text>
|
||||
<rect x="600" y="84" width="140" height="58" rx="6" fill="#C6ECE9" stroke="#7DBCB7"/>
|
||||
<text x="670" y="102" class="c sub b">一致的状态</text>
|
||||
<text x="670" y="120" class="c sub mono">A = 70</text>
|
||||
<text x="670" y="136" class="c sub mono">B = 30</text>
|
||||
<line x1="172" y1="116" x2="216" y2="116" class="arrow "/>
|
||||
<text x="196" y="106" class="c sub mono" style="font-size:10px">A − 30</text>
|
||||
<line x1="362" y1="116" x2="406" y2="116" class="arrow "/>
|
||||
<text x="386" y="106" class="c sub mono" style="font-size:10px">B + 30</text>
|
||||
<line x1="552" y1="116" x2="596" y2="116" class="arrow "/>
|
||||
<text x="576" y="106" class="c sub mono" style="font-size:10px">COMMIT</text>
|
||||
<path d="M290,144 V186 H100 V148" class="arrow red dash"/>
|
||||
<path d="M480,144 V186 H100 V148" class="arrow red dash"/>
|
||||
<text x="250" y="206" class="sub note">出错或执行 ROLLBACK:用 undo log 撤销已做的修改,回到初始状态</text>
|
||||
<text x="30" y="254" class="sub">在提交读及以上的隔离级别下,中间状态只有当前事务看得到;提交后修改才对其它事务可见,并由 redo log 保证不会丢失。</text>
|
||||
<text x="30" y="274" class="sub">A、B 两个账户的余额总和在事务前后都是 100,这就是一致性。</text>
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 3.7 KiB |
@@ -0,0 +1,63 @@
|
||||
<svg xmlns="http://www.w3.org/2000/svg" width="780" height="426" viewBox="0 0 780 426">
|
||||
<style>
|
||||
text { font-family: "PingFang SC", "Microsoft YaHei", Helvetica, Arial, sans-serif; fill: #3F3F3F; font-size: 12px; }
|
||||
.title { font-weight: bold; font-size: 15px; fill: #333; text-anchor: middle; }
|
||||
.b { font-weight: bold; }
|
||||
.c { text-anchor: middle; }
|
||||
.e { text-anchor: end; }
|
||||
.sub { font-size: 11px; fill: #6E6E6E; }
|
||||
.note { fill: #C0605A; }
|
||||
.ok { fill: #6A9F68; }
|
||||
.mono { font-family: Menlo, Consolas, monospace; }
|
||||
.arrow { stroke: #555; stroke-width: 1.1; fill: none; marker-end: url(#a); }
|
||||
.red { stroke: #C0605A; marker-end: url(#r); }
|
||||
.green { stroke: #6A9F68; marker-end: url(#g); }
|
||||
.dash { stroke-dasharray: 5 4; }
|
||||
.line { stroke: #555; stroke-width: 1.1; fill: none; }
|
||||
.frame { fill: none; stroke: #999; stroke-width: 1; stroke-dasharray: 4 3; }
|
||||
.shadow { fill: #DADADA; }
|
||||
</style>
|
||||
<defs>
|
||||
<marker id="a" viewBox="0 0 10 10" refX="9" refY="5" markerWidth="7" markerHeight="7" orient="auto-start-reverse">
|
||||
<path d="M1,1.5 L9.5,5 L1,8.5 z" fill="#555"/>
|
||||
</marker>
|
||||
<marker id="r" viewBox="0 0 10 10" refX="9" refY="5" markerWidth="7" markerHeight="7" orient="auto-start-reverse">
|
||||
<path d="M1,1.5 L9.5,5 L1,8.5 z" fill="#C0605A"/>
|
||||
</marker>
|
||||
<marker id="g" viewBox="0 0 10 10" refX="9" refY="5" markerWidth="7" markerHeight="7" orient="auto-start-reverse">
|
||||
<path d="M1,1.5 L9.5,5 L1,8.5 z" fill="#6A9F68"/>
|
||||
</marker>
|
||||
</defs>
|
||||
<rect width="100%" height="100%" fill="#FFFFFF"/>
|
||||
|
||||
<text x="390" y="28" class="title">丢失修改:两个事务基于同一个旧值计算后写回</text>
|
||||
<rect x="40" y="50" width="335" height="34" rx="6" fill="#C6E4FB" stroke="#7FB2D6"/>
|
||||
<text x="207.5" y="65" class="c sub b">事务 T1:取出 30 元</text>
|
||||
<text x="207.5" y="80" class="sub c">REPEATABLE READ</text>
|
||||
<line x1="207.5" y1="84" x2="207.5" y2="352" stroke="#D2D2D2" stroke-dasharray="4 3"/>
|
||||
<rect x="405" y="50" width="335" height="34" rx="6" fill="#C6ECE9" stroke="#7DBCB7"/>
|
||||
<text x="572.5" y="65" class="c sub b">事务 T2:存入 50 元</text>
|
||||
<text x="572.5" y="80" class="sub c">REPEATABLE READ</text>
|
||||
<line x1="572.5" y1="84" x2="572.5" y2="352" stroke="#D2D2D2" stroke-dasharray="4 3"/>
|
||||
<text x="22" y="104" class="c sub">时间</text>
|
||||
<line x1="22" y1="110" x2="22" y2="346" class="arrow"/>
|
||||
<rect x="46" y="96" width="323" height="26" rx="5" fill="#FFFFFF" stroke="#7FB2D6"/>
|
||||
<text x="56" y="113" class="sub mono" style="font-size:10px;fill:#3F3F3F">SELECT balance FROM account WHERE id = 1</text>
|
||||
<text x="359" y="113" class="e sub mono" style="font-size:10px">→ 100</text>
|
||||
<rect x="411" y="132" width="323" height="26" rx="5" fill="#FFFFFF" stroke="#7DBCB7"/>
|
||||
<text x="421" y="149" class="sub mono" style="font-size:10px;fill:#3F3F3F">SELECT balance FROM account WHERE id = 1</text>
|
||||
<text x="724" y="149" class="e sub mono" style="font-size:10px">→ 100</text>
|
||||
<rect x="46" y="168" width="323" height="26" rx="5" fill="#FFFFFF" stroke="#7FB2D6"/>
|
||||
<text x="56" y="185" class="sub mono" style="font-size:10px;fill:#3F3F3F">UPDATE account SET balance = 70 WHERE id = 1</text>
|
||||
<rect x="46" y="204" width="323" height="26" rx="5" fill="#FFFFFF" stroke="#7FB2D6"/>
|
||||
<text x="56" y="221" class="sub mono" style="font-size:10px;fill:#3F3F3F">COMMIT</text>
|
||||
<rect x="411" y="240" width="323" height="26" rx="5" fill="#FFFFFF" stroke="#7DBCB7"/>
|
||||
<text x="421" y="257" class="sub mono" style="font-size:10px;fill:#3F3F3F">UPDATE account SET balance = 150 WHERE id = 1</text>
|
||||
<rect x="411" y="276" width="323" height="26" rx="5" fill="#FFFFFF" stroke="#7DBCB7"/>
|
||||
<text x="421" y="293" class="sub mono" style="font-size:10px;fill:#3F3F3F">COMMIT</text>
|
||||
<rect x="46" y="312" width="323" height="26" rx="5" fill="#FFFFFF" stroke="#7FB2D6"/>
|
||||
<text x="56" y="329" class="sub mono" style="font-size:10px;fill:#3F3F3F">SELECT balance FROM account WHERE id = 1</text>
|
||||
<text x="359" y="329" class="e sub mono note b" style="font-size:10px">→ 150</text>
|
||||
<text x="30" y="372" class="sub b" style="fill:#3F3F3F">正确结果应为 120,T1 取出的 30 元丢失了。</text>
|
||||
<text x="30" y="394" class="sub" style="">改为 SET balance = balance - 30 后,T2 的 UPDATE 会等待 T1 的行锁,再在 70 的基础上加 50,结果为 120。</text>
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 4.3 KiB |
@@ -0,0 +1,58 @@
|
||||
<svg xmlns="http://www.w3.org/2000/svg" width="780" height="354" viewBox="0 0 780 354">
|
||||
<style>
|
||||
text { font-family: "PingFang SC", "Microsoft YaHei", Helvetica, Arial, sans-serif; fill: #3F3F3F; font-size: 12px; }
|
||||
.title { font-weight: bold; font-size: 15px; fill: #333; text-anchor: middle; }
|
||||
.b { font-weight: bold; }
|
||||
.c { text-anchor: middle; }
|
||||
.e { text-anchor: end; }
|
||||
.sub { font-size: 11px; fill: #6E6E6E; }
|
||||
.note { fill: #C0605A; }
|
||||
.ok { fill: #6A9F68; }
|
||||
.mono { font-family: Menlo, Consolas, monospace; }
|
||||
.arrow { stroke: #555; stroke-width: 1.1; fill: none; marker-end: url(#a); }
|
||||
.red { stroke: #C0605A; marker-end: url(#r); }
|
||||
.green { stroke: #6A9F68; marker-end: url(#g); }
|
||||
.dash { stroke-dasharray: 5 4; }
|
||||
.line { stroke: #555; stroke-width: 1.1; fill: none; }
|
||||
.frame { fill: none; stroke: #999; stroke-width: 1; stroke-dasharray: 4 3; }
|
||||
.shadow { fill: #DADADA; }
|
||||
</style>
|
||||
<defs>
|
||||
<marker id="a" viewBox="0 0 10 10" refX="9" refY="5" markerWidth="7" markerHeight="7" orient="auto-start-reverse">
|
||||
<path d="M1,1.5 L9.5,5 L1,8.5 z" fill="#555"/>
|
||||
</marker>
|
||||
<marker id="r" viewBox="0 0 10 10" refX="9" refY="5" markerWidth="7" markerHeight="7" orient="auto-start-reverse">
|
||||
<path d="M1,1.5 L9.5,5 L1,8.5 z" fill="#C0605A"/>
|
||||
</marker>
|
||||
<marker id="g" viewBox="0 0 10 10" refX="9" refY="5" markerWidth="7" markerHeight="7" orient="auto-start-reverse">
|
||||
<path d="M1,1.5 L9.5,5 L1,8.5 z" fill="#6A9F68"/>
|
||||
</marker>
|
||||
</defs>
|
||||
<rect width="100%" height="100%" fill="#FFFFFF"/>
|
||||
|
||||
<text x="390" y="28" class="title">不可重复读:同一个事务两次读取同一行,结果不同</text>
|
||||
<rect x="40" y="50" width="335" height="34" rx="6" fill="#C6E4FB" stroke="#7FB2D6"/>
|
||||
<text x="207.5" y="65" class="c sub b">事务 T1</text>
|
||||
<text x="207.5" y="80" class="sub c">自动提交</text>
|
||||
<line x1="207.5" y1="84" x2="207.5" y2="280" stroke="#D2D2D2" stroke-dasharray="4 3"/>
|
||||
<rect x="405" y="50" width="335" height="34" rx="6" fill="#C6ECE9" stroke="#7DBCB7"/>
|
||||
<text x="572.5" y="65" class="c sub b">事务 T2</text>
|
||||
<text x="572.5" y="80" class="sub c">READ COMMITTED</text>
|
||||
<line x1="572.5" y1="84" x2="572.5" y2="280" stroke="#D2D2D2" stroke-dasharray="4 3"/>
|
||||
<text x="22" y="104" class="c sub">时间</text>
|
||||
<line x1="22" y1="110" x2="22" y2="274" class="arrow"/>
|
||||
<rect x="411" y="96" width="323" height="26" rx="5" fill="#FFFFFF" stroke="#7DBCB7"/>
|
||||
<text x="421" y="113" class="sub mono" style="font-size:10px;fill:#3F3F3F">BEGIN</text>
|
||||
<rect x="411" y="132" width="323" height="26" rx="5" fill="#FFFFFF" stroke="#7DBCB7"/>
|
||||
<text x="421" y="149" class="sub mono" style="font-size:10px;fill:#3F3F3F">SELECT balance FROM account WHERE id = 1</text>
|
||||
<text x="724" y="149" class="e sub mono" style="font-size:10px">→ 100</text>
|
||||
<rect x="46" y="168" width="323" height="26" rx="5" fill="#FFFFFF" stroke="#7FB2D6"/>
|
||||
<text x="56" y="185" class="sub mono" style="font-size:10px;fill:#3F3F3F">UPDATE account SET balance = 200 WHERE id = 1</text>
|
||||
<rect x="411" y="204" width="323" height="26" rx="5" fill="#FFFFFF" stroke="#7DBCB7"/>
|
||||
<text x="421" y="221" class="sub mono" style="font-size:10px;fill:#3F3F3F">SELECT balance FROM account WHERE id = 1</text>
|
||||
<text x="724" y="221" class="e sub mono note b" style="font-size:10px">→ 200</text>
|
||||
<rect x="411" y="240" width="323" height="26" rx="5" fill="#FFFFFF" stroke="#7DBCB7"/>
|
||||
<text x="421" y="257" class="sub mono" style="font-size:10px;fill:#3F3F3F">COMMIT</text>
|
||||
<text x="30" y="300" class="sub b" style="fill:#3F3F3F">T1 在 T2 的两次读取之间修改并提交了这一行。</text>
|
||||
<text x="30" y="322" class="sub" style="">T2 的隔离级别为 REPEATABLE READ 时,第二次读到的仍然是 100。</text>
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 3.7 KiB |
@@ -0,0 +1,58 @@
|
||||
<svg xmlns="http://www.w3.org/2000/svg" width="780" height="354" viewBox="0 0 780 354">
|
||||
<style>
|
||||
text { font-family: "PingFang SC", "Microsoft YaHei", Helvetica, Arial, sans-serif; fill: #3F3F3F; font-size: 12px; }
|
||||
.title { font-weight: bold; font-size: 15px; fill: #333; text-anchor: middle; }
|
||||
.b { font-weight: bold; }
|
||||
.c { text-anchor: middle; }
|
||||
.e { text-anchor: end; }
|
||||
.sub { font-size: 11px; fill: #6E6E6E; }
|
||||
.note { fill: #C0605A; }
|
||||
.ok { fill: #6A9F68; }
|
||||
.mono { font-family: Menlo, Consolas, monospace; }
|
||||
.arrow { stroke: #555; stroke-width: 1.1; fill: none; marker-end: url(#a); }
|
||||
.red { stroke: #C0605A; marker-end: url(#r); }
|
||||
.green { stroke: #6A9F68; marker-end: url(#g); }
|
||||
.dash { stroke-dasharray: 5 4; }
|
||||
.line { stroke: #555; stroke-width: 1.1; fill: none; }
|
||||
.frame { fill: none; stroke: #999; stroke-width: 1; stroke-dasharray: 4 3; }
|
||||
.shadow { fill: #DADADA; }
|
||||
</style>
|
||||
<defs>
|
||||
<marker id="a" viewBox="0 0 10 10" refX="9" refY="5" markerWidth="7" markerHeight="7" orient="auto-start-reverse">
|
||||
<path d="M1,1.5 L9.5,5 L1,8.5 z" fill="#555"/>
|
||||
</marker>
|
||||
<marker id="r" viewBox="0 0 10 10" refX="9" refY="5" markerWidth="7" markerHeight="7" orient="auto-start-reverse">
|
||||
<path d="M1,1.5 L9.5,5 L1,8.5 z" fill="#C0605A"/>
|
||||
</marker>
|
||||
<marker id="g" viewBox="0 0 10 10" refX="9" refY="5" markerWidth="7" markerHeight="7" orient="auto-start-reverse">
|
||||
<path d="M1,1.5 L9.5,5 L1,8.5 z" fill="#6A9F68"/>
|
||||
</marker>
|
||||
</defs>
|
||||
<rect width="100%" height="100%" fill="#FFFFFF"/>
|
||||
|
||||
<text x="390" y="28" class="title">幻影读:同一个事务两次范围查询,行数不同</text>
|
||||
<rect x="40" y="50" width="335" height="34" rx="6" fill="#C6E4FB" stroke="#7FB2D6"/>
|
||||
<text x="207.5" y="65" class="c sub b">事务 T1</text>
|
||||
<text x="207.5" y="80" class="sub c">自动提交</text>
|
||||
<line x1="207.5" y1="84" x2="207.5" y2="280" stroke="#D2D2D2" stroke-dasharray="4 3"/>
|
||||
<rect x="405" y="50" width="335" height="34" rx="6" fill="#C6ECE9" stroke="#7DBCB7"/>
|
||||
<text x="572.5" y="65" class="c sub b">事务 T2</text>
|
||||
<text x="572.5" y="80" class="sub c">READ COMMITTED</text>
|
||||
<line x1="572.5" y1="84" x2="572.5" y2="280" stroke="#D2D2D2" stroke-dasharray="4 3"/>
|
||||
<text x="22" y="104" class="c sub">时间</text>
|
||||
<line x1="22" y1="110" x2="22" y2="274" class="arrow"/>
|
||||
<rect x="411" y="96" width="323" height="26" rx="5" fill="#FFFFFF" stroke="#7DBCB7"/>
|
||||
<text x="421" y="113" class="sub mono" style="font-size:10px;fill:#3F3F3F">BEGIN</text>
|
||||
<rect x="411" y="132" width="323" height="26" rx="5" fill="#FFFFFF" stroke="#7DBCB7"/>
|
||||
<text x="421" y="149" class="sub mono" style="font-size:10px;fill:#3F3F3F">SELECT id FROM account WHERE balance > 50</text>
|
||||
<text x="724" y="149" class="e sub mono" style="font-size:10px">→ 1,2</text>
|
||||
<rect x="46" y="168" width="323" height="26" rx="5" fill="#FFFFFF" stroke="#7FB2D6"/>
|
||||
<text x="56" y="185" class="sub mono" style="font-size:10px;fill:#3F3F3F">INSERT INTO account VALUES (3, 100)</text>
|
||||
<rect x="411" y="204" width="323" height="26" rx="5" fill="#FFFFFF" stroke="#7DBCB7"/>
|
||||
<text x="421" y="221" class="sub mono" style="font-size:10px;fill:#3F3F3F">SELECT id FROM account WHERE balance > 50</text>
|
||||
<text x="724" y="221" class="e sub mono note b" style="font-size:10px">→ 1,2,3</text>
|
||||
<rect x="411" y="240" width="323" height="26" rx="5" fill="#FFFFFF" stroke="#7DBCB7"/>
|
||||
<text x="421" y="257" class="sub mono" style="font-size:10px;fill:#3F3F3F">COMMIT</text>
|
||||
<text x="30" y="300" class="sub b" style="fill:#3F3F3F">T1 在 T2 查询的范围内插入了新的一行。</text>
|
||||
<text x="30" y="322" class="sub" style="">T2 的隔离级别为 REPEATABLE READ 时,第二次查询仍然只有 1、2。</text>
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 3.7 KiB |