92 lines
6.1 KiB
Markdown
92 lines
6.1 KiB
Markdown
# 事务
|
||
## 事务分类
|
||
事务通常可分为如下类型:
|
||
- 扁平事务(flat transaction)
|
||
- 带保存点的扁平事务(flat transaction with savepoints)
|
||
- 链事务(chained transaction)
|
||
- 嵌套事务(nested transaction)
|
||
- 分布式事务(distributed transaction)
|
||
|
||
### 扁平事务
|
||
扁平事务为最常用的事务,在扁平事务中,所有操作都处于统一层次,其由`begin work`开始,并由`commit work`或`rollback work`结束。
|
||
|
||
扁平事务的操作是原子的,要么都提交,要么都回滚。
|
||
|
||
### 带有保存点的扁平事务
|
||
对于带有保存点的扁平事务,`其支持在事务执行过程中回滚到同一事务中较早的一个状态`。
|
||
|
||
在事务执行过程中,可能并不希望所有的操作都回滚,放弃所有操作的代价可能太大。通过`保存点`,可以记住事务当前的状态,在后续发生错误后,事务能够回到保存点当时的状态。
|
||
|
||
相较于扁平事务只能够全部回滚,带保存点的扁平事务能够回滚到保存点时的状态。
|
||
|
||
保存点可以通过`save work`来创建,使用示例如下所示
|
||
|
||
<img alt="" height="752" src="https://i-blog.csdnimg.cn/blog_migrate/2ef380492373bb8e4a6e529109baee3c.png" width="728">
|
||
|
||
### 链事务
|
||
可视为保存点事务的一个变种。在使用带保存点的事务时,如果系统发生崩溃,那么所有的保存点都会消失。`在后续重启进行恢复时,事务需要从开始处重新执行`,而不是从最近的一个保存点开始执行。
|
||
|
||
> 若事务在数据库未提交时发生崩溃,那么在数据库再次重启执行recovery操作时,会对未提交的事务进行回滚,即使之前事务存在保存点,也会全部回滚
|
||
|
||
链事务的思想是,当提交事务时,释放不必要的数据对象,将必要的上下文隐式传递给下一个要开始的事务。`提交事务和开始下一个事务操作必须为原子操作`,下一个事务必须徐要能看到上一个事务的结果,示例如下:
|
||
|
||
<img class="trans" src="https://images2015.cnblogs.com/blog/754297/201602/754297-20160204112125600-267403241.jpg">
|
||
|
||
和带保存点的扁平事务不同的是,带保存点的扁平事务能够回滚到任意正确的保存点,而链事务只能回滚当前事务。
|
||
|
||
且链事务和带保存点的扁平事务,对于锁的处理也不同:
|
||
- `链事务`:对于每个事务,commit后释放持有的锁
|
||
- `带保存点的扁平事务`:在整个事务提交前,不会释放持有的锁
|
||
|
||
### 嵌套事务
|
||
嵌套事务为一个层次结构框架,由顶层事务控制各个层次的事务。嵌套在顶层事务中的事务被称为`子事务`。
|
||
|
||
<img src="https://pic2.zhimg.com/v2-01f00b04df29181143da399008b92055_r.jpg">
|
||
|
||
如下为Moss理论嵌套事务的定义:
|
||
- 嵌套事务是由若干事务组成的一颗树,子树既可以是嵌套事务,又可以是扁平事务
|
||
- 处在叶子节点的事务是扁平事务
|
||
- 位于根节点的事务被称为顶层事务,其他事务被称为子事务,事务的`predecessor`被称为父事务,事务的下一层事务被称为子事务
|
||
- 子事务既可以提交又可以回滚,但是子事务的提交并不会立马生效,除非其父事务已经被提交。`任何子事务都在顶层事务提交后才真正提交`
|
||
- 树中任何一个事务的回滚会引其所有子事务都一起回滚
|
||
|
||
在Moss理论中,实际工作被交由叶子节点来完成,`只有叶子节点的事务才能够访问数据库,发送消息,获取其他类型的资源`。`高层事务仅仅负责逻辑控制,即负责何时调用相关子事务`。
|
||
|
||
即使一个系统不支持嵌套事务,也可以通过保存点技术来模拟嵌套事务。
|
||
|
||
#### savepoint和嵌套事务区别
|
||
在使用保存点来模拟嵌套事务时,在锁持有方面和嵌套事务有差别。
|
||
- 嵌套事务:在使用嵌套事务时,不同子事务在数据库持有的锁不同
|
||
- 保存点:在通过保存点来模拟嵌套事务时,用户无法选择哪些锁被哪些子事务继承,无论有多少个保存点,所有的锁都可以得到访问
|
||
|
||
|
||
### 分布式事务
|
||
通常是在分布式环境运行的扁平事务,需要访问网络中的不同节点。
|
||
|
||
分布式事务同样需要满足ACID的特性,要么都发生,要么都不发生。
|
||
|
||
对于innodb存储引擎,其支持扁平事务、带有保存点的扁平事务、链事务、分布式事务。`innodb并不原生支持嵌套事务,单可以通过带保存点的事务来模拟串行的嵌套事务`。
|
||
|
||
## 事务实现
|
||
对于事务的ACID特性,其实现如下:
|
||
- `I(隔离性)`:事务隔离性通过锁来实现
|
||
- `A(原子性), D(持久性)`:事务的原子性和持久性可以通过redo log来实现
|
||
- `C(一致性)`:事务一致性通过undo log来实现
|
||
|
||
### redo
|
||
redo log(重做日志)用于实现事务的持久性,其由两部分组成:
|
||
- 内存中的重做日志缓冲(redo log buffer)
|
||
- 磁盘中的重做日志文件(redo log file)
|
||
|
||
innodb存储引擎支持事务,其通过`force log at commit`机制实现事务的持久性,即当事务提交时,必须先将该事务的所有日志写入到磁盘的日志文件中进行持久化,直到该过程完成后事务才提交完成。
|
||
|
||
> 上述描述中`提交时将事务所有日志写入到磁盘的日志文件中`,这句话中`日志`代表`redo log 和 undo log`。
|
||
> - redo log用于保证事务持久性
|
||
> - undo log用于帮助事务回滚,也用于mvcc功能
|
||
|
||
redo log基本都是顺序写的,在数据库进程运行时不需要对redo log进行读取操作;而数据库进行需要对undo log进行随机读写。
|
||
|
||
#### innodb_flush_log_at_trx_commit
|
||
参数`innodb_flush_log_at_trx_commit`用于控制redo log/ undo log刷新到磁盘的策略,该参数默认值为`1`,即事务提交时刷新日志到磁盘。除了默认值之外,还可以为该参数设置如下值:
|
||
- 0: 事务提交时不刷新日志到磁盘,仅在master thread中刷新日志到磁盘,master thread中刷新操作每秒触发一次
|
||
- 1:事务提交时刷新日志,但是仅将日志写入到文件系统的缓存中,`并不进行fsync操作` |