<commit_before>---
layout: post
category: Mysql
title: mysql原理3-事务
tags: Mysql
---

## 一、基础概念

> [参考](https://www.cnblogs.com/kismetv/p/10331633.html)

梳理下事务执行的各个阶段：

（1）写undo日志到log buffer；

（2）执行事务，并写redo日志到log buffer；

（3）如果innodb_flush_log_at_trx_commit=1(自动提交)，则将redo日志写到log file，并刷新落盘指redolog。

（4）提交事务。

可能有同学会问，为什么没有写data file，事务就提交了？

在数据库的世界里，数据从来都不重要，日志才是最重要的，有了日志就有了一切。



### 1. 逻辑架构和存储引擎

![img](https://cdn.jsdelivr.net/gh/mafulong/mdPic@vv1/v1/175.png)

如上图所示，MySQL服务器逻辑架构从上往下可以分为三层：

（1）第一层：处理客户端连接、授权认证等。

（2）第二层：服务器层，负责查询语句的解析、优化、缓存以及内置函数的实现、存储过程等。

（3）第三层：存储引擎，负责MySQL中数据的存储和提取。MySQL中服务器层不管理事务，事务是由存储引擎实现的。MySQL支持事务的存储引擎有InnoDB、NDB Cluster等，其中InnoDB的使用最为广泛；其他存储引擎不支持事务，如MyIsam、Memory等。

如无特殊说明，后文中描述的内容都是基于InnoDB。



### 2. 提交和回滚

典型的MySQL事务是如下操作的：

```
start ``transaction``;``…… #一条或多条sql语句``commit``;
```

其中start transaction标识事务开始，commit提交事务，将执行结果写入到数据库。如果sql语句执行出现问题，会调用rollback，回滚所有已经执行成功的sql语句。当然，也可以在事务中直接使用rollback语句进行回滚。

**自动提交**

MySQL中默认采用的是自动提交（autocommit）模式，如下所示：

![img](https://cdn.jsdelivr.net/gh/mafulong/mdPic@vv1/v1/75.png)

在自动提交模式下，如果没有start transaction显式地开始一个事务，那么每个sql语句都会被当做一个事务执行提交操作。

通过如下方式，可以关闭autocommit；需要注意的是，autocommit参数是针对连接的，在一个连接中修改了参数，不会对其他连接产生影响。

![img](https://cdn.jsdelivr.net/gh/mafulong/mdPic@vv1/v1/75.png)

如果关闭了autocommit，则所有的sql语句都在一个事务中，直到执行了commit或rollback，该事务结束，同时开始了另外一个事务。

**特殊操作**

在MySQL中，存在一些特殊的命令，如果在事务中执行了这些命令，会马上强制执行commit提交事务；如DDL语句(create table/drop table/alter/table)、lock tables语句等等。

不过，常用的select、insert、update和delete命令，都不会强制提交事务。

### 3. ACID特性

> 1. **脏读** ：脏读就是指当一个事务正在访问数据，并且对数据进行了修改，而这种修改还没有提交到数据库中，这时，另外一个事务也访问这个数据，然后使用了这个数据。
>
> 2. **不可重复读** ：是指在一个事务内，多次读同一数据。在这个事务还没有结束时，另外一个事务也访问该同一数据。那么，在第一个事务中的两次读数据之间，由于第二个事务的修改，那么第一个事务两次读到的的数据可能是不一样的。这样在一个事务内两次读到的数据是不一样的，因此称为是不可重复读。
>
> 3. **幻读** : 是指当事务不是独立执行时发生的一种现象，例如第一个事务对一个表中的数据进行了修改，这种修改涉及到表中的全部数据行。同时，第二个事务也修改这个表中的数据，这种修改是向表中插入一行新数据。那么，以后就会发生操作第一个事务的用户发现表中还有没有修改的数据行，就好象发生了幻觉一样。

ACID是衡量事务的四个特性：

- 原子性（Atomicity）：事务被视为不可分割的最小单元，事务的所有操作要么全部提交成功，要么全部失败回滚。

  - 回滚可以用回滚日志（Undo Log）来实现，回滚日志记录着事务所执行的修改操作，在回滚时反向执行这些修改操作即可。

- 一致性（Consistency）：数据库在事务执行前后都保持一致性状态。在一致性状态下，所有事务对同一个数据的读取结果都是相同的。

- 隔离性（Isolation）：多个事务并发执行时，一个事务的执行不应影响其他事务的执行。一个事务所做的修改在最终提交以前，对其它事务是不可见的。

  - **与原子性、持久性侧重于研究事务本身不同，隔离性研究的是不同事务之间的相互影响。**隔离性是指，事务内部的操作与其他事务是隔离的，并发执行的各个事务之间不能互相干扰。严格的隔离性，对应了事务隔离级别中的Serializable (可串行化)，但实际应用中出于性能方面的考虑很少会使用可串行化。

    隔离性追求的是并发情形下事务之间互不干扰。简单起见，我们主要考虑最简单的读操作和写操作(加锁读等特殊读操作会特殊说明)，那么隔离性的探讨，主要可以分为两个方面：

    - (一个事务)写操作对(另一个事务)写操作的影响：锁机制保证隔离性
    - (一个事务)写操作对(另一个事务)读操作的影响：MVCC保证隔离性

- 持久性（Durability）：一旦事务提交，则其所做的修改将会永远保存到数据库中。即使系统发生崩溃，事务执行的结果也不能丢失。

  系统发生崩溃可以用重做日志（Redo Log）进行恢复，从而实现持久性。与回滚日志记录数据的逻辑修改不同，重做日志记录的是数据页的物理修改。



事务的 ACID 特性概念简单，但不是很好理解，主要是因为这几个特性不是一种平级关系：

- 只有满足一致性，事务的执行结果才是正确的。
- 在无并发的情况下，事务串行执行，隔离性一定能够满足。此时只要能满足原子性，就一定能满足一致性。
- 在并发的情况下，多个事务并行执行，事务不仅要满足原子性，还需要满足隔离性，才能满足一致性。
- 事务满足持久化是为了能应对系统崩溃的情况。

<commit_msg>add logbook<commit_after>---
layout: post
category: Mysql
title: mysql原理3-事务
tags: Mysql
---

## 一、基础概念

> [参考](https://www.cnblogs.com/kismetv/p/10331633.html)

梳理下事务执行的各个阶段：

（1）写undo日志到log buffer；

（2）执行事务，并写redo日志到log buffer；

（3）如果innodb_flush_log_at_trx_commit=1(自动提交)，则将redo日志写到log file，并刷新落盘指redolog。

（4）提交事务。

可能有同学会问，为什么没有写data file，事务就提交了？

在数据库的世界里，数据从来都不重要，日志才是最重要的，有了日志就有了一切。



### 1. 逻辑架构和存储引擎

![img](https://cdn.jsdelivr.net/gh/mafulong/mdPic@vv1/v1/175.png)

如上图所示，MySQL服务器逻辑架构从上往下可以分为三层：

（1）第一层：处理客户端连接、授权认证等。

（2）第二层：服务器层，负责查询语句的解析、优化、缓存以及内置函数的实现、存储过程等。

（3）第三层：存储引擎，负责MySQL中数据的存储和提取。MySQL中服务器层不管理事务，事务是由存储引擎实现的。MySQL支持事务的存储引擎有InnoDB、NDB Cluster等，其中InnoDB的使用最为广泛；其他存储引擎不支持事务，如MyIsam、Memory等。

如无特殊说明，后文中描述的内容都是基于InnoDB。



### 2. 提交和回滚

典型的MySQL事务是如下操作的：

```
start ``transaction``;``…… #一条或多条sql语句``commit``;
```

其中start transaction标识事务开始，commit提交事务，将执行结果写入到数据库。如果sql语句执行出现问题，会调用rollback，回滚所有已经执行成功的sql语句。当然，也可以在事务中直接使用rollback语句进行回滚。

**自动提交**

MySQL中默认采用的是自动提交（autocommit）模式，如下所示：

![img](https://cdn.jsdelivr.net/gh/mafulong/mdPic@vv1/v1/75.png)

在自动提交模式下，如果没有start transaction显式地开始一个事务，那么每个sql语句都会被当做一个事务执行提交操作。

通过如下方式，可以关闭autocommit；需要注意的是，autocommit参数是针对连接的，在一个连接中修改了参数，不会对其他连接产生影响。

![img](https://cdn.jsdelivr.net/gh/mafulong/mdPic@vv1/v1/75.png)

如果关闭了autocommit，则所有的sql语句都在一个事务中，直到执行了commit或rollback，该事务结束，同时开始了另外一个事务。

**特殊操作**

在MySQL中，存在一些特殊的命令，如果在事务中执行了这些命令，会马上强制执行commit提交事务；如DDL语句(create table/drop table/alter/table)、lock tables语句等等。

不过，常用的select、insert、update和delete命令，都不会强制提交事务。

### 3. ACID特性

> 1. **脏读** ：脏读就是指当一个事务正在访问数据，并且对数据进行了修改，而这种修改还没有提交到数据库中，这时，另外一个事务也访问这个数据，然后使用了这个数据。
>
> 2. **不可重复读** ：是指在一个事务内，多次读同一数据。在这个事务还没有结束时，另外一个事务也访问该同一数据。那么，在第一个事务中的两次读数据之间，由于第二个事务的修改，那么第一个事务两次读到的的数据可能是不一样的。这样在一个事务内两次读到的数据是不一样的，因此称为是不可重复读。
>
> 3. **幻读** : 是指当事务不是独立执行时发生的一种现象，例如第一个事务对一个表中的数据进行了修改，这种修改涉及到表中的全部数据行。同时，第二个事务也修改这个表中的数据，这种修改是向表中插入一行新数据。那么，以后就会发生操作第一个事务的用户发现表中还有没有修改的数据行，就好象发生了幻觉一样。

ACID是衡量事务的四个特性：

- 原子性（Atomicity）：事务被视为不可分割的最小单元，事务的所有操作要么全部提交成功，要么全部失败回滚。

  - 回滚可以用回滚日志（Undo Log）来实现，回滚日志记录着事务所执行的修改操作，在回滚时反向执行这些修改操作即可。

- 一致性（Consistency）：数据库在事务执行前后都保持一致性状态。在一致性状态下，所有事务对同一个数据的读取结果都是相同的。

  - 如何理解一致性？应用系统从一个正确的状态到另一个正确的状态

  - 数据库一致性，是数据库中的数据开始是正确的，随着状态转移，总是保持正确的状态。用户在任意时刻的任何请求返回的都是正确的结果。数据库以一定的模式存储数据，本质是对真实世界建模，因此这里的正确是指，数据满足真实世界各种约束（例如：完整性约束）。 

    事务一致性，事务开始前数据库处于一致状态，结束后数据库依旧满足一致性。

    数据库状态转移的基本单位是事务，为了保证数据正确，状态转移之间不应相互干扰（应依次执行），否则数据会出现不确定的结果（想象一下1000个线程没有保护的同时修改一个数据）。这也是就是很多资料里将一致性描述为：“事务开始和结束之间的中间状态不会被其他事务看到”的原因了。

- 隔离性（Isolation）：多个事务并发执行时，一个事务的执行不应影响其他事务的执行。一个事务所做的修改在最终提交以前，对其它事务是不可见的。

  - **与原子性、持久性侧重于研究事务本身不同，隔离性研究的是不同事务之间的相互影响。**隔离性是指，事务内部的操作与其他事务是隔离的，并发执行的各个事务之间不能互相干扰。严格的隔离性，对应了事务隔离级别中的Serializable (可串行化)，但实际应用中出于性能方面的考虑很少会使用可串行化。

    隔离性追求的是并发情形下事务之间互不干扰。简单起见，我们主要考虑最简单的读操作和写操作(加锁读等特殊读操作会特殊说明)，那么隔离性的探讨，主要可以分为两个方面：

    - (一个事务)写操作对(另一个事务)写操作的影响：锁机制保证隔离性
    - (一个事务)写操作对(另一个事务)读操作的影响：MVCC保证隔离性

- 持久性（Durability）：一旦事务提交，则其所做的修改将会永远保存到数据库中。即使系统发生崩溃，事务执行的结果也不能丢失。

  系统发生崩溃可以用重做日志（Redo Log）进行恢复，从而实现持久性。与回滚日志记录数据的逻辑修改不同，重做日志记录的是数据页的物理修改。



事务的 ACID 特性概念简单，但不是很好理解，主要是因为这几个特性不是一种平级关系：

- 只有满足一致性，事务的执行结果才是正确的。
- 在无并发的情况下，事务串行执行，隔离性一定能够满足。此时只要能满足原子性，就一定能满足一致性。
- 在并发的情况下，多个事务并行执行，事务不仅要满足原子性，还需要满足隔离性，才能满足一致性。
- 事务满足持久化是为了能应对系统崩溃的情况。


{'fraction_non_alphanumeric': 0.12594541269319304, 'fraction_numerical': 0.012495889510029596, 'mean_word_length': 10.720616570327554, 'pattern_counts': {'":': 0, '<': 3, '<?xml version=': 0, '>': 15, 'https://': 8, 'lorem ipsum': 0, 'www.': 2, 'xml': 0}, 'pii_count': 0, 'substrings_counts': 0, 'word_list_counts': {'cursed_substrings.json': 0, 'profanity_word_list.json': 0, 'sexual_word_list.json': 0, 'zh_pornsignals.json': 0}}
{'dir': 'git-commits-cleaned', 'id': '2974761', 'n_tokens_mistral': 4968, 'n_tokens_neox': 5680, 'n_words': 175}