带你看看事务的分类和在MySQL中使用链式事务 | SQL全面教程七:事务(3)事务分类和MySQL中的commit work and chain

2,720 阅读8分钟

本文已参与好文召集令活动,点击查看:后端、大前端双赛道投稿,2万元奖池等你挑战!

本篇主要参考自《MySQL技术内幕:innodb存储引擎》、维基百科、相关官方文档等内容。

事务分类

从形式上,事务分为以下几类。

  • 扁平事务(Flat Transactions)
  • 带有保存点的扁平事务(Flat Transactions with Savepoints)
  • 链式事务(Chained Transactions)
  • 嵌套事务(Nested Transactions)
  • 分布式事务(Distributed Transactions)

当然还有多级事务(Multilevel Transactions)。

事务的这种分类,也可以描述为事务的模型(models),即不同模型的事务。

扁平事务

扁平事务是最常见的一种。扁平事务中所有操作都处于同一层次,由Start Transaction/BEGIN WORK开始,由commit [work]rollback [work]结束,期间的操作时原子的,要么都执行、要么都不执行。

扁平事务三种不同的结果,分别是commit,rollback,等待超时。扁平事务的主要限制是不能提交或者回滚一部分,或者分几步提交。

带有保存点的扁平事务

除了支持扁平事务外,还能在事务的回滚中回滚到一个较早的状态。

保存点(Savepoint)用来系统记录当前事务的状态,以便发生错误时,事务能回滚到保存点当时的状态。SAVEPOINT savepoint_name;save work命令设置保存点。

扁平事务默认有一个保存点1,也就是事务开始的地方。

链事务

在提交一个事务时,释放不需要的数据对象,将必要的处理上下文隐式地传给下一个要开始的事务。注意:提交事务操作和下一个事务开始操作合并为一个原子操作,这意味着下一个事务将看到上一个事务的结果。

链事务的工作方式如下图所示:

链事务只能回滚当前事务,即恢复到最近的一个事务开始点。链事务在执行commit操作后即释放了当前事务持有的锁。而带有保存点的扁平事务在创建保存点时不影响迄今为止持有的锁。

链事务可以看作保存点模式的一种变种,带保存点的扁平事务,当发生系统崩溃或故障时,所有的保存点都会消失,因为保存点是易失的(volatile),而非持久的(persistent)。这表示,当进行恢复需要重做日志时,事务需要从开始处重新执行,而无法从最近的一个保留点继续执行。

链事务是串行执行的,有着特定的顺序。

嵌套事务(Nested transactions)

嵌套事务是一个层次结构框架,由一个顶层事务(top-level transaction)控制着各个层次的事务。顶层事务之下嵌套的事务称为子事务(subtransaction)。

嵌套事务层次结构图如下图所示:

  • 嵌套事务是由若干事务组成的一棵树,子树既可以是嵌套事务,也可以是扁平事务
  • 处在叶节点的事务是扁平事务。
  • 处于根节点的事务为顶层事务,其他称为子事务。事务的前驱(predecessor)为父事务(parent),事务的下一层称为子事务(child)。
  • 子事务既可以提交也可以回滚。但它的提交不会立马生效,除非其父事务提交。因此,任何子事务都在顶层事务提交后才真正的提交。
  • 树中任意一个事务的回滚会引起它的所有子事务一同回滚,故子事务仅保留A、C、I特性,不具有D的特性。

在最外层事务提交之前,任何不相关的事务都不会看到更改。这意味着内部事务中的提交不一定会将更新持久化到系统中。

即使一个RDBMS不支持嵌套事务,也可以通过保留点来模拟嵌套事务。但是,当通过保留点模拟嵌套事务时,用户无法选择哪些锁需要被子事务继承,哪些需要被父事务保留。而嵌套事务中,不同子事务在数据库对象上持有的锁是不同。

MySQL的InnoDB不支持嵌套事务,PostgreSQL也不支持嵌套事务

嵌套事务提供了真正基于组件的应用程序架构的能力。组件函数可能包含也可能不包含数据库事务,如果在BEGIN-COMMIT括号内调用含事务的组件函数,则会发生嵌套事务。但是由于MySQL等流行数据库不支持嵌套事务,因此需要一个框架或事务监视器(transaction monitor)来处理这个问题。

(此部分 无需关注)

  • "Closed Nesting" 封闭嵌套:子事务的提交最终依据顶层事务是否提交。SQL Server数据库系统就是这种形式的嵌套。
  • "Open Nesting" 开放嵌套:子事务可以独立于父事务进行提交。也就是如果父事务回滚,成功的子事务的结果不会回滚。父事务的提交或回滚,不影响子事务的提交。

分布式事务

分布式事务通常是一个在分布式环境中运行的扁平事务。需要根据数据所在位置访问网络中的不同节点。

(比如,跨行转账,会涉及到分布式事务),分布式事务同样需要满足ACID特性。

InnoDB存储引擎提供XA事务,由此支持分布式事务的实现。

分布式事务指的是允许多个独立的事务资源(transactional resources,如RDBMS等)参与到一个全局的事务中。这其中涉及到多个独立的资源管理器(resource manager),通常指数据库,还涉及一个事务管理器(transaction manager),用于协调管理多个资源尤其是跨服务器资源组成的全局事务。全局事务要求其中所有的事务要么都提交,要么都回滚。

  • 资源管理器:用来管理系统资源,是通向事务资源的途径。数据库就是一种资源管理器。资源管理还应该具有管理事务提交或回滚的能力。
  • 事务管理器:事务管理器是分布式事务的核心管理者。事务管理器与每个资源管理器进行通信,协调并完成事务的处理。事务的各个分支由唯一命名进行标识。

XA协议由Tuxedo首先提出的,并交给X/Open组织的分布式事务的规范或者协议标准

后续会对分布式事务进行详细介绍。

MySQL的InnoDB原生并不支持嵌套事务,但是支持扁平事务、带保留点的事务、链事务和分布式事务。PostgreSQL不支持嵌套事务。SQL Server支持扁平事务、带保留点的事务、分布式和嵌套事务,但不支持链式事务。

关于多级事务(Multi-level Transactions)

多级事务属于嵌套事务的一种,或者说是嵌套事务的变体。大多数数据库文档或SQL概念中,并没有将其列为特定的技术术语,或在数据库系统中专门实现它。

多级事务是嵌套事务的一种变体,其中子事务发生在分层系统架构的不同级别(例如,一个操作在数据库引擎级别,一个操作在操作系统级别)

Multi-level transactions are a variant of nested transactions where the sub-transactions take place at different levels of a layered system architecture (e.g., with one operation at the database-engine level, one operation at the operating-system level)

(en.wikipedia.org/wiki/Databa…)

具体可以查看 research.microsoft.com/en-us/um/pe… 中的介绍。

关于MySQL中的commit workcommit work and chain

MySQL/MariaDB中可以使用start transaction的别名begin work开启一个事务。

事务提交和回滚除了commitrollback,还可以使用commit workrollback work。默认情况下,加不加work都是等价的。

commit work and chain;用于开启一个链式事务,即提交的同时开始一个新的事务。

使用START TRANSACTION;/begin显式开启一个事务,如果执行commit会提交事务;如果执行commit work and chain;则会提交事务并自动启动下一个事务,这样可以省去了再次执行START TRANSACTION;begin work的开销,较少了语句交互的次数,并且也明确标识语句处于事务中。

  • commitcommit work的不同取决于completion_type参数的设置
    1. completion_type=0:二者完全等价(也是默认方式),表示提交当前事务。
    2. completion_type=1commit work等同于commit and chain,表示提交当前事务,并马上开启一个相同隔离级别的事务。
    3. completion_type=2commit work等同于commit and release,表示提交事务后会自动断开与服务器的连接。

rollback和rollback work与commit和commit work的执行完全相同。

  • 查看completion_type参数设置:
MariaDB [(none)]> show variables like 'completion_type';
+-----------------+----------+
| Variable_name   | Value    |
+-----------------+----------+
| completion_type | NO_CHAIN |
+-----------------+----------+
1 row in set (0.025 sec)
  • 设置completion_type参数为1:
MariaDB [(none)]> set @@completion_type=1;
Query OK, 0 rows affected (0.008 sec)

MariaDB [(none)]> show variables like 'completion_type';
+-----------------+-------+
| Variable_name   | Value |
+-----------------+-------+
| completion_type | CHAIN |
+-----------------+-------+
1 row in set (0.001 sec)