Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

test #219

Closed
wants to merge 1 commit into from
Closed

test #219

Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -3,18 +3,18 @@
## 为什么需要事务?

在许多大型、关键的应用程序中,计算机每秒钟都在执行大量的任务。更为经常的不是这些任务本身,而是将这些任务结合在一起完成一个业务要求,称为事务。如果能成功地执行一个任务,而在第二个或第三个相关的任务中出现错误,将会发生什么?这个错误很可能使系统数据处于不一致状态。这时事务变得非常重要,它能使系统数据摆脱这种不一致的状态。
如何理解事务呢?例如在某家银行的银行系统中,如果没有事务对数据进行控制和管理,很可能出现 A 从企业账户中取出一笔钱,同时 B 和 C 也从同一企业账户中取钱。每一笔转账涉及到最少两个账户信息的变化例如,A 的钱到账,企业账户出账;B 的钱到账,企业账户出账;C 的钱到账,企业账户出账,如果没有事务,那么账面金额的具体数值将无法确定。在引入事务这一业务要求之后,事务的基本特性ACID确保了银行账面的资金操作是原子性不可再分割的,其他人看到的金额是具备隔离性的,每一次操作都是具有一致性的,所有操作是持久性的,这样保证了银行系统数据出入账保持一致。
如何理解事务呢?例如在某家银行的银行系统中,如果没有事务对数据进行控制和管理,很可能出现 A 从企业账户中取出一笔钱,同时 B 和 C 也从同一企业账户中取钱。每一笔转账涉及到最少两个账户信息的变化 (例如,A 的钱到账,企业账户出账;B 的钱到账,企业账户出账;C 的钱到账,企业账户出账),如果没有事务,那么账面金额的具体数值将无法确定。在引入事务这一业务要求之后,事务的基本特性 (ACID) 确保了银行账面的资金操作是原子性 (不可再分割) 的,其他人看到的金额是具备隔离性的,每一次操作都是具有一致性的,所有操作是持久性的,这样保证了银行系统数据出入账保持一致。

## 什么是事务?

数据库事务即,Transaction,事务是数据库管理系统执行过程中的一个逻辑单位,由一个有限的数据库操作序列构成。
数据库事务 (即,Transaction,事务) 是数据库管理系统执行过程中的一个逻辑单位,由一个有限的数据库操作序列构成。
事务就是作为一个逻辑单位提交或者回滚一系列的 SQL 语句。

## 事务的特征

通常事务需要具备 ACID 四个特征:

- **原子性Atomicity**:事务的原子性是指事务是一个不可分割的单位,在一个事务中的操作要么都发生,要么都不发生。
- **原子性 (Atomicity)**:事务的原子性是指事务是一个不可分割的单位,在一个事务中的操作要么都发生,要么都不发生。

例如,我们现在有如下事务:

Expand All @@ -27,7 +27,7 @@

如果对 t1 插入数据或修改 t2 数据中的任意一条发生错误,整个事务都会回滚,而只有两条语句同时成功时,才会提交成功,不会出现一个操作成功而另一个操作失败。

- **一致性Consistency**:事务的一致性是指在事务前后,数据必须是保持正确并遵守所有数据相关的约束。
- **一致性 (Consistency)**:事务的一致性是指在事务前后,数据必须是保持正确并遵守所有数据相关的约束。

例如,我们在数据库中建立一个新表

Expand All @@ -41,7 +41,7 @@
insert into t1 values(1,'abcde'),(2,'bcdef');
```

- **隔离性Isolation**:事务的隔离性是在多个用户并发访问时,事务之间要遵守规定好的隔离级别,在确定的隔离级别范围内,一个事务不能被另一个事务所干扰。
- **隔离性 (Isolation)**:事务的隔离性是在多个用户并发访问时,事务之间要遵守规定好的隔离级别,在确定的隔离级别范围内,一个事务不能被另一个事务所干扰。

例如,如下事务示例,会话隔离级别是读已提交,在会话 1 能看到的数据如下:

Expand Down Expand Up @@ -94,7 +94,7 @@
+------+------+
```

- **持久性Durability**:事务的持久性是指,在数据库中一个事务被提交时,它对数据库中的数据的改变是永久性的,无论数据库软件是否重启。
- **持久性 (Durability)**:事务的持久性是指,在数据库中一个事务被提交时,它对数据库中的数据的改变是永久性的,无论数据库软件是否重启。

## 事务的分类

Expand All @@ -115,7 +115,7 @@

无论是乐观事务,还是悲观事务,其事务的执行结果都是一样的,即一个事务中的操作,对 ACID 级别的要求,完全一样,无论是原子性、一致性、隔离性或者持久性,都是完全一致,不存在乐观事务就宽松一些,悲观事务就严格一些的情况。

乐观事务与悲观事务的区别,它只是两阶段提交基于待处理业务状态的不同执行策略,其选择基于执行者的判断,其效率高低基于被处理业务的实际状态并发事务的写冲突频繁度。即在于对于事务相关资源的状态做出不同的假设,从而将写锁放在不同的阶段中。
乐观事务与悲观事务的区别,它只是两阶段提交基于待处理业务状态的不同执行策略,其选择基于执行者的判断,其效率高低基于被处理业务的实际状态 (并发事务的写冲突频繁度)。即在于对于事务相关资源的状态做出不同的假设,从而将写锁放在不同的阶段中。

在乐观事务开始时,会假定事务相关的表处于一个不会发生写冲突的状态,把对数据的插入、修改或删除缓存在内存中,在这一阶段不会对数据加锁,而在数据提交时对相应的数据表或数据行上锁,在完成提交后解锁。

Expand All @@ -138,7 +138,7 @@ MatrixOne Cloud 的悲观事务详情可以参见 [MatrixOne Cloud 的悲观事

在数据库事务的 ACID 四个属性中,隔离性是一个限制最宽松的。为了获取更高的隔离等级,数据库系统通常使用锁机制或者多版本并发控制机制。应用软件也需要额外的逻辑来使其正常工作。

很多数据库管理系统DBMS定义了不同的“事务隔离等级”来控制锁的程度。在很多数据库系统中,多数的事务都避免高等级的隔离等级如可串行化从而减少锁的开销。程序员需要小心的分析数据库访问部分的代码来保证隔离级别的降低不会造成难以发现的代码错误。相反的,更高的隔离级别会增加死锁发生的几率,同样需要在编程过程中去避免。
很多数据库管理系统 (DBMS) 定义了不同的 “事务隔离等级” 来控制锁的程度。在很多数据库系统中,多数的事务都避免高等级的隔离等级 (如可串行化) 从而减少锁的开销。程序员需要小心的分析数据库访问部分的代码来保证隔离级别的降低不会造成难以发现的代码错误。相反的,更高的隔离级别会增加死锁发生的几率,同样需要在编程过程中去避免。

由于更高的隔离级别中不存在被一个更低的隔离级别禁止的操作,DBMS 被允许使用一个比请求的隔离级别更高的隔离级别。

Expand All @@ -151,13 +151,13 @@ ANSI/ISO SQL 定义的标准隔离级别共有四个:
|REPEATABLE READ|Not Possible|Not Possible|Not Possible| Possible|
|SERIALIZABLE|Not Possible|Not Possible|Not Possible|Not Possible|

- **读未提交**:读未提交READ UNCOMMITTED是最低的隔离级别。允许“脏读”dirty reads,事务可以看到其他事务“尚未提交”的修改。
- **读未提交**:读未提交 (READ UNCOMMITTED) 是最低的隔离级别。允许 “脏读” (dirty reads),事务可以看到其他事务 “尚未提交” 的修改。

- **读已提交**:读已提交READ COMMITTED级别中,基于锁机制并发控制的 DBMS 需要对选定对象的写锁一直保持到事务结束,但是读锁在 SELECT 操作完成后马上释放。和前一种隔离级别一样,也不要求“范围锁”。
- **读已提交**:读已提交 (READ COMMITTED) 级别中,基于锁机制并发控制的 DBMS 需要对选定对象的写锁一直保持到事务结束,但是读锁在 SELECT 操作完成后马上释放。和前一种隔离级别一样,也不要求 “范围锁”。

- **可重复读**:在可重复读REPEATABLE READS隔离级别中,基于锁机制并发控制的 DBMS 需要对选定对象的读锁read locks和写锁write locks一直保持到事务结束,但不要求“范围锁”,因此可能会发生“幻读”。MatrixOne Cloud 实现了快照隔离即 Snapshot Isolation,为了与 MySQL 隔离级别保持一致,MatrixOne Cloud 快照隔离又叫做可重复读REPEATABLE READS
- **可重复读**:在可重复读 (REPEATABLE READS) 隔离级别中,基于锁机制并发控制的 DBMS 需要对选定对象的读锁 (read locks) 和写锁 (write locks) 一直保持到事务结束,但不要求 “范围锁”,因此可能会发生 “幻读”。MatrixOne Cloud 实现了快照隔离 (即 Snapshot Isolation),为了与 MySQL 隔离级别保持一致,MatrixOne Cloud 快照隔离又叫做可重复读 (REPEATABLE READS)

- **可串行化**:可串行化SERIALIZABLE是最高的隔离级别。在基于锁机制并发控制的 DBMS 上,可串行化要求在选定对象上的读锁和写锁直到事务结束后才能释放。在 SELECT 的查询中使用一个“WHERE”子句来描述一个范围时应该获得一个“范围锁”range-locks
- **可串行化**:可串行化 (SERIALIZABLE) 是最高的隔离级别。在基于锁机制并发控制的 DBMS 上,可串行化要求在选定对象上的读锁和写锁直到事务结束后才能释放。在 SELECT 的查询中使用一个 “WHERE” 子句来描述一个范围时应该获得一个 “范围锁” (range-locks)

通过比低一级的隔离级别要求更多的限制,高一级的级别提供更强的隔离性。标准允许事务运行在更强的事务隔离级别上。

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -5,9 +5,9 @@
## 显式事务规则

- 显式事务是指以 `BEGIN...END` 或 `START TRANSACTION...COMMIT` 或 `ROLLBACK` 作为起始结束。
- 在显式事务中,DMLData Manipulation Language,数据操纵语言与 DDLData Definition Language,数据库定义语言可以同时存在,支持除数据库与序列之外的所有对象类型的 DDL。
- 在显式事务中,DML (Data Manipulation Language,数据操纵语言) 与 DDL (Data Definition Language,数据库定义语言) 可以同时存在,支持除数据库与序列之外的所有对象类型的 DDL。
- 显式事务中,无法嵌套其他显式事务,例如 `START TRANSACTIONS` 之后再遇到 `START TRANSACTIONS`,两个 `START TRANSACTIONS` 之间的所有语句都会强制提交,无论 `AUTOCOMMIT` 的值是 1 或 0。
- 显式事务中,不能存在 `SET` 命令与管理类命令`CREATE USER/ROLE` 或 `GRANT`,只能包含 DML 与 DDL。
- 显式事务中,不能存在 `SET` 命令与管理类命令 (`CREATE USER/ROLE` 或 `GRANT`),只能包含 DML 与 DDL。
- 显式事务中,如果在未发生显式提交或回滚而开启一个新事务而发生写写冲突,之前未提交的事务将会回滚并报错。

## 与 MySQL 显式事务的区别
Expand Down Expand Up @@ -63,7 +63,7 @@ mysql> SELECT * FROM Accounts;

MatrixOne Cloud 支持跨库事务行为,这里将以一个简单的示例进行说明。

首先,让我们创建两个数据库db1 和 db2以及它们各自的表table1 和 table2
首先,让我们创建两个数据库 (db1 和 db2) 以及它们各自的表 (table1 和 table2)

```sql
-- 创建 db1 数据库
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,7 @@

## MatrixOne Cloud 与 MySQL 隐式事务的区别

在 MatrixOne Cloud 中,如果开启了隐式事务`SET AUTOCOMMIT=0`,则所有操作都需要手动执行 `COMMIT` 或 `ROLLBACK` 来结束事务。相比之下,MySQL 在遇到 DDL 或类似 `SET` 语句时会自动提交。
在 MatrixOne Cloud 中,如果开启了隐式事务 (`SET AUTOCOMMIT=0`),则所有操作都需要手动执行 `COMMIT` 或 `ROLLBACK` 来结束事务。相比之下,MySQL 在遇到 DDL 或类似 `SET` 语句时会自动提交。

### MySQL 隐式事务行为

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@

## 读已提交

MatrixOne Cloud 默认**读已提交Read Committed)**隔离级别,它的特点如下:
MatrixOne Cloud 默认**读已提交 (Read Committed)** 隔离级别,它的特点如下:

- 在不同的事务之间,只能读到其他事务已经提交的数据,对于未提交的数据,无法查看。
- 读已提交的隔离级别,能够有效防止脏写和脏读,但是不能避免不可重复读与幻读。
Expand Down Expand Up @@ -112,7 +112,7 @@ SELECT * FROM t1;

## 快照隔离

在 MatrixOne Cloud 中,也支持快照隔离Snapshot Isolation,为了与 MySQL 隔离级别保持一致,MatrixOne Cloud 快照隔离又叫做可重复读REPEATABLE READS。该级别的隔离实现原理如下:
在 MatrixOne Cloud 中,也支持快照隔离 (Snapshot Isolation),为了与 MySQL 隔离级别保持一致,MatrixOne Cloud 快照隔离又叫做可重复读 (REPEATABLE READS)。该级别的隔离实现原理如下:

### 快照隔离原理

Expand Down
Original file line number Diff line number Diff line change
@@ -1,13 +1,13 @@
# MVCC

MVCCMultiversion Concurrency Control,多版本并发控制应用于 MatrixOne,以保证事务快照隔离,实现事务的隔离性。
MVCC (Multiversion Concurrency Control,多版本并发控制) 应用于 MatrixOne,以保证事务快照隔离,实现事务的隔离性。

基于数据元组Tuple,即表中的每行的指针字段来创建一个 Latch Free 的链表,称为版本链。这个版本链允许数据库定位一个 Tuple 的所需版本。因此这些版本数据的存放机制是数据库存储引擎设计的一个重要考量。
基于数据元组 (Tuple,即表中的每行) 的指针字段来创建一个 Latch Free 的链表,称为版本链。这个版本链允许数据库定位一个 Tuple 的所需版本。因此这些版本数据的存放机制是数据库存储引擎设计的一个重要考量。

一个方案是采用 Append Only 机制,一个表的所有 Tuple 版本都存储在同一个存储空间。这种方法被用于 Postgre SQL,为了更新一个现有的 Tuple,数据库首先为新的版本从表中获取一个空的槽Slot,然后,它将当前版本的内容复制到新版本。最后,它在新分配的 Slot 中应用对 Tuple 的修改。Append Only 机制的关键是决定如何为 Tuple 的版本链排序,由于不可能维持一个无锁Lock free的双向链表,因此版本链只指向一个方向,或者从 Old 到 NewO2N,或者从 New 到 OldN2O
一个方案是采用 Append Only 机制,一个表的所有 Tuple 版本都存储在同一个存储空间。这种方法被用于 Postgre SQL,为了更新一个现有的 Tuple,数据库首先为新的版本从表中获取一个空的槽 (Slot),然后,它将当前版本的内容复制到新版本。最后,它在新分配的 Slot 中应用对 Tuple 的修改。Append Only 机制的关键是决定如何为 Tuple 的版本链排序,由于不可能维持一个无锁 (Lock free) 的双向链表,因此版本链只指向一个方向,或者从 Old 到 New (O2N),或者从 New 到 Old (N2O)

另外一个类似的方案称为时间旅行Time Travel,它会把版本链的信息单独存放,而主表维护主版本数据。
另外一个类似的方案称为时间旅行 (Time Travel),它会把版本链的信息单独存放,而主表维护主版本数据。

第三种方案,是在主表中维护 Tuple 的主版本,在一个单独的数据库对比工具Delta存储中维护一系列 Delta 版本。这种存储在 MySQL 和 Oracle 中被称为回滚段。为了更新一个现有的 Tuple,数据库从 Delta 存储中获取一个连续的空间来创建一个新的 Delta 版本。这个 Delta 版本包含修改过的属性的原始值,而不是整个 Tuple。然后数据库直接对主表中的主版本进行原地更新In Place Update
第三种方案,是在主表中维护 Tuple 的主版本,在一个单独的数据库对比工具 (Delta) 存储中维护一系列 Delta 版本。这种存储在 MySQL 和 Oracle 中被称为回滚段。为了更新一个现有的 Tuple,数据库从 Delta 存储中获取一个连续的空间来创建一个新的 Delta 版本。这个 Delta 版本包含修改过的属性的原始值,而不是整个 Tuple。然后数据库直接对主表中的主版本进行原地更新 (In Place Update)

![image-20221026152318567](https://community-shared-data-1308875761.cos.ap-beijing.myqcloud.com/artwork/docs/distributed-transaction/mvcc.png)
Original file line number Diff line number Diff line change
Expand Up @@ -35,7 +35,7 @@ MatrixOne Cloud 支持乐观事务模型。你在使用乐观并发读取一行

2. 在下午 1:01,用户 2 从数据库中读取同一行。

3. 在下午 1:03,用户 2 将 FirstName 列的“Bob”改为“Robert”,并更新到数据库里。
3. 在下午 1:03,用户 2 将 FirstName 列的 “Bob” 改为 “Robert”,并更新到数据库里。

|Column name|Original value|Current value|Value in database|
|---|---|---|---|
Expand All @@ -45,12 +45,12 @@ MatrixOne Cloud 支持乐观事务模型。你在使用乐观并发读取一行

4. 如上表所示,更新时数据库中的值与用户 2 的原始值匹配,表示更新成功。

5. 在下午 1:05,用户 1 将 FirstName 列的“Bob”改为“James”,并尝试进行更新。
5. 在下午 1:05,用户 1 将 FirstName 列的 “Bob” 改为 “James”,并尝试进行更新。

|Column name|Original value|Current value|Value in database|
|---|---|---|---|
|CustID|101|101|101|
|LastName|Smith|Smith|Smith|
|FirstName|Bob|James|Robert|

6. 此时,用户 1 遇到了乐观并发冲突,因为数据库中的值“Robert”不再与用户 1 期望的原始值“Bob”匹配,并发冲突提示更新失败。下一步需要决定,是采用用户 1 的更改覆盖用户 2 的更改,还是取消用户 1 的更改。
6. 此时,用户 1 遇到了乐观并发冲突,因为数据库中的值 “Robert” 不再与用户 1 期望的原始值 “Bob” 匹配,并发冲突提示更新失败。下一步需要决定,是采用用户 1 的更改覆盖用户 2 的更改,还是取消用户 1 的更改。
Loading