乐趣区

关于数据库:连载如何掌握openGauss数据库核心技术秘诀四拿捏事务机制1

目录
openGauss 数据库 SQL 引擎

openGauss 数据库执行器技术

openGauss 存储技术

openGauss 事务机制

Ⅰ.openGauss 数据库事务概览
1. 显示事务和隐式事务
2. 单机事务和分布式事务
Ⅱ.openGauss 事务 ACID 个性介绍
Ⅲ.openGauss 并发管制
Ⅳ.openGauss 分布式事务
openGauss 数据库安全

openGauss 事务机制

事务是数据库为用户提供的最外围、最具吸引力的性能之一。简略地说,事务是用户定义的一系列数据库操作(如查问、插入、批改或删除等)的汇合,数据库从外部保障了该操作汇合(作为一个整体)的原子性(Atomicity)、一致性(Consistency)、隔离性(Isolation)和持久性(Durability),统称事务的 ACID 个性。其中:

§ A:原子性是指一个事务中的所有操作要么全副执行胜利,要么全副执行失败。一个事务执行当前,数据库只可能处于上述两种状态之一。即便数据库在这些操作执行过程中产生故障,数据库也不会呈现只有局部操作执行胜利的状态。

§ C:一致性是指一个事务的执行会导致数据从一个统一的状态转移到另一个统一的状态,事务的执行不会违反一致性束缚、触发器等定义的规定。

§ I:隔离性是指在一个事务的执行过程中,所看到的数据库状态受并发事务的影响水平。依据该影响水平的轻重,个别将事务的隔离级别分为读未提交、读已提交、可反复读和可串行化四个级别(受并发事务影响由重到轻)。

§ D:持久性是指一旦一个事务的提交当前,那么即便数据库产生故障重启,该事务的执行后果不会失落,依然对后续事务可见。

本章次要联合 openGauss 数据库的事务机制和实现原理,来论述在 openGauss 是如何保障事务的 ACID 个性的。

一.openGauss 数据库事务概览

通过前序文章的介绍,大家曾经晓得 openGauss 是一个分布式的数据库。同样的,openGauss 数据库的事务机制也是一个从单机到分布式的双层构架。

图 1 openGauss 集群事务组件形成示意图

如图 1 所示,在 openGauss 集群中,事务的执行和治理次要波及 GTM、CN 和 DN 三种组件,其中:

§ GTM,全称 Global Transaction Manager,即全局事务管理器,负责全局事务号的散发,事务提交工夫戳的散发以及全局事务运行状态的注销。对于采纳多版本并发管制(Multi-Version Concurrency Control,简称 MVCC)的事务模型(以 openGauss 和 Oracle 为例),GTM 实质上能够简化为一个递增序列号(或工夫戳)生成器,其为集群的所有事务进行了全局的对立排序,以确定快照(Snapshot)内容和由此决定的事务可见性。在本章第三节 openGauss 数据库并发管制中,将进一步详述 GTM 的作用。

§ CN,全称 Coordinator Node,即协调者实例,负责管理和推动一个具体事务的执行流程,保护和推动事务执行的事务块状态机。

§ DN,全称 Data Node,即数据实例,负责一个具体事务在某一个数据分片内的所有读写操作。

本节次要介绍:

§ 显式事务和隐式事务执行流程中,CN 和 DN 上事务块状态机的推演。

§ 单机事务和分布式事务的异同。

显式事务和隐式事务 01
§ 显式事务是指,用户在所执行的一条或多条 SQL 语句的前后,显式增加了开启事务 START TRANSACTION 语句和提交事务 COMMIT 语句。

§ 隐式事务是指,用户在所执行的一条或多条 SQL 语句的前后,没有显式增加开启事务和提交事务的语句。在这种状况下,每一条 SQL 语句在开始执行时,openGauss 外部都会为其开启一个事务,并且在该语句执行实现之后,主动提交该事务。

以一条 SELECT 语句和一条 INSERT 语句为例,简要形容显式事务和隐式事务在 openGauss 集群中的次要执行流程。

显式事务的 SQL 语句如下(假如表 t 只蕴含一个整数类型字段 a,且为散布列):

START TRANSACTION;
SELECT * FROM t;
INSERT INTO t(a) VALUES (100);
COMMIT;
1)START TRANSACTION
该 SQL 语句只在 CN 上执行,CN 显式开启一个事务,并将 CN 本地事务块状态机从闲暇状态置为进行中状态,而后返回客户端,期待下一条 SQL 命令。

2)SELECT * FROM t
该 SQL 语句首先在 CN 上执行,因为 openGauss 分片采纳一致性哈希算法,因而对于不带散布列上谓词条件的查问语句,CN 须要将该 SQL 语句发送到所有 DN 实例上执行。对于每一个分片对应的 DN 实例,因为采纳了显式事务,CN 会先发送一条 START TRANSACTION 命令给该 DN,让该 DN 显式开启事务(DN 上的事务块状态机从闲暇状态变为进行中状态),而后 CN 将 SELECT 语句发送给该 DN。尔后,CN 在收到所有 DN 的查问后果之后,返回客户端,期待下一条 SQL 命令。

3)INSERT INTO t(a) VALUES (100)
该 SQL 语句首先在 CN 上执行,因为 a 为表 t 的散布列,因而 CN 能够依据被插入记录中 a 的具体取值,来决定应该由哪个数据分片对应的 DN 实例来执行理论的插入操作(这里假如该分片为 DN1)。因为采纳了显式事务,CN 先发送一条 START TRANSACTION 命令给 DN1,因为通过第(2)步 DN1 的事务块状态机曾经处于进行中状态,因而对于该语句 DN1 并不会执行什么理论的操作,而后,CN 将具体的 INSERT 语句发送给 DN1,并期待 DN1 执行插入胜利之后,返回客户端,期待下一条 SQL 命令。

4)COMMIT
该 SQL 语句首先在 CN 上执行,CN 进入提交事务阶段后,将 COMMIT 语句发送给所有参加第(2)步和第(3)步的 DN,让这些 DN 完结该事务,并将 DN 本地的事务块状态机从进行中状态置为闲暇状态。CN 在收到所有 DN 的事务提交后果之后,再将 CN 本地的事务块状态机从进行中状态置为闲暇状态。而后,CN 返回客户端,该事务执行实现。

上述操作的隐式事务语句如下(假如表 t 只蕴含一个整数类型字段 a,且为散布列):

SELECT * FROM t;
INSERT INTO t(a) VALUES (1);
1)SELECT * FROM t
该 SQL 语句首先在 CN 上执行,CN 隐式开启一个事务,将 CN 本地的事务块状态机从闲暇状态置为开启状态(留神不同于显式事务中的进行中状态)。而后,CN 须要将该语句发送到所有 DN 实例上执行。对于每一个分片对应的 DN 实例,因为采纳了隐式事务且该语句为只读查问,CN 间接将 SELECT 语句发送给该 DN。

DN 收到该 SELECT 语句之后,亦采纳隐式事务:第一步,隐式开启事务,将 DN 本地的事务块状态机从闲暇状态置为开启状态;第二步,执行该查问语句,将查问后果返回给 CN;第三步,隐式提交事务,将 DN 本地的事务块状态机从开启状态置为闲暇状态。

CN 在收到所有 DN 的查问后果之后,返回客户端,并隐式提交事务,将 CN 本地的事务块状态机从开启状态置为闲暇状态。

2)INSERT INTO t(a) VALUES (1)
该 SQL 语句首先在 CN 上执行,CN 隐式开启一个事务,将 CN 本地的事务块状态机从闲暇状态置为开启状态。而后,CN 须要将该 INSERT 语句发送到目标分片的 DN 实例上执行(这里假如该分片为 DN1)。

尽管该语句采纳了隐式事务,然而因为该语句为写操作,因而在 DN1 上会采取显式事务:CN 会先发送一条 START TRANSACTION 命令给 DN1,让 DN1 显式开启事务(DN1 上的事务块状态机从闲暇状态变为进行中状态),而后 CN 将 INSERT 语句发送给 DN1,DN1 执行实现后,返回执行后果给 CN。

CN 收到执行后果之后,进入提交事务阶段。先发送 COMMIT 语句到 DN1。DN1 收到 COMMIT 语句后,进行显式提交,将 DN1 本地的事务块状态机从进行中状态置为闲暇状态。CN 在收到 DN1 的事务提交后果之后,本地再进行隐式提交事务,将 CN 本地的事务块状态机从开启状态置为闲暇状态,返回客户端,该事务执行实现。

综上,对于 CN 来说,应用显式事务还是隐式事务,齐全取决于用户输出的 SQL 语句;对于 DN 来说,只有当 SQL 为隐式只读事务时,才会应用隐式事务,当 SQL 为显式事务或者隐式写事务时,都会应用显式事务。

单机事务和分布式事务 02
在 openGauss 这样的分布式集群中,单机事务(亦称单分片事务)是指一个事务中所有的操作都产生在同一个分片(即 DN 实例)上,分布式事务是指一个事务中有两个或以上的分片参加了该事务的执行。

对于单机事务,其写操作的原子性和读操作的一致性由该 DN 本身的事务机制就能保障;对于分布式事务,不同分片之间写操作的原子性和不同分片之间读操作的一致性,须要额定的机制来保障。上面联合 SQL 语句简要介绍下分布式事务的原子性和一致性要求,具体的原理机制将在后续文章的第四节中阐明。

首先,思考波及多分片的写操作事务,以如下事务 T1 为例(假如表 t 只蕴含一个整数类型字段 a,且为散布列):

START TRANSACTION;
INSERT INTO t(a) VALUES (v1);
INSERT INTO t(a) VALUES (v2);
COMMIT;
下面事务 T1 的两条 INSERT 语句均为只波及一个分片的写(插入)事务,如果 v1 和 v2 散布在同一个分片内,那么该事务为单机事务,如果 v1 和 v2 散布在两个不同的分片内,那么该事务为分布式事务。

对于只波及一个 DN 分片的单机事务,其对于数据库的批改和影响全副产生在同一个分片内,因而该分片的事务提交后果即是该事务在整个集群的提交后果,该分片事务提交的原子性就可能保障整个事务的原子性。在事务 T1 示例中,如果 v1 和 v2 全散布在 DN1 上,那么在 DN1 上,如果事务提交,那么这两条记录就全副插入胜利;如果 DN1 上事务回滚,那么这两条记录的插入就全副失败。

对于分布式事务,为了保障事务在整个集群范畴内的原子性,必须保障所有参加写操作的分片要么全副提交,那么全副回滚,不能呈现局部分片提交,局部分片回滚的“两头态”。如图 2 所示,如果 v1 插入到 DN1 上,且 DN1 提交胜利,同时,v2 插入到 DN2 上,且 DN2 最终回滚,那么最终该事务只有一部分操作胜利,毁坏了事务的原子性要求。为了防止这种状况的产生,openGauss 采纳两阶段提交(Two Phase Commit,简称 2PC)协定,来保障分布式事务的原子性,在后续文章的第四节中会对两阶段提交相干内容进行更具体的介绍。

图 2 分布式事务原子性问题示意图

其次,思考波及多分片的读操作事务 T2,以如下 SQL 语句为例(假如表 t 只蕴含一个整数类型字段 a,且为散布列):

START TRANSACTION;
SELECT * FROM t where a = v1 or a = v2;
COMMIT;
下面查问事务 T2 中,如果 v1 和 v2 散布在同一个分片内,那么该事务为单机事务,如果 v1 和 v2 散布在两个不同的分片内,那么该事务为分布式事务。

对于单机事务,其查问的数据齐全来自于同一个分片内,因而该分片事务的可见性和一致性就可能保障整个事务的一致性。

在事务 T1 和 T2 示例中,思考 T1 和 T2 并发执行的场景(假如 T1 提交胜利),如果 v1 和 v2 全散布在 DN1 上,那么,在 DN1 上,如果 T1 对 T2 可见,那么 T2 就能查问到所有的两条记录,如果 T1 对 T2 不可见,那么 T2 不会查问到两条记录中的任何一条。

对于分布式事务,其查问的数据来自不同的分片,单个分片的可见性和一致性无奈齐全保障整个事务的一致性,不同分片之间事务提交的先后顺序和可见性判断会导致查问后果存在某种“不确定性”。

仍思考 T1 和 T2 并发执行的场景(假如 T1 提交胜利)。如图 3 所示,如果 v1 和 v2 别离散布在 DN1 和 DN2 上,若在 DN1 上,T1 事务提交先于 T2 的查问执行,且对于 T2 可见,而在 DN2 上,T2 的查问执行先于 T1 事务提交(或 T1 事务提交先于 T2 查问执行,但对 T2 不可见),那么 T2 最终只会查问到 v1 这一条记录。对于以银行为代表的传统数据库用户来说,这种景象毁坏了事务作为一个整体的一致性要求。在分布式事务中,亦称为强一致性要求。

图 3 分布式事务一致性问题示意图

另一方面,如果 T1 先实现提交,并期待足够长的工夫当前(保障所有分片均实现 T1 的提交,并保障提交后果对 T2 可见),再执行 T2,那么 T2 将能够看到 T1 插入的所有两条记录。在分布式事务中,这种一致性体现被称为最终一致性。与传统数据库用户不同,在互联网等新兴业务中,最终一致性是被宽泛承受的。

openGauss 通过全局一致性的工夫戳(快照)技术和本地两阶段事务弥补技术,提供分布式强统一事务的能力,同时,对于谋求性能的新兴数据库业务,也反对可选的最终一致性事务的能力。

未完待续 ……

退出移动版