共计 3943 个字符,预计需要花费 10 分钟才能阅读完成。
目录
openGauss 数据库 SQL 引擎
openGauss 数据库执行器技术
openGauss 存储技术
openGauss 事务机制
Ⅰ.openGauss 数据库事务概览
Ⅱ.openGauss 事务 ACID 个性介绍
1.openGauss 中的事务持久性
2.openGauss 中的事务原子性
3.openGauss 中的事务一致性
4.openGauss 中的事务隔离性
Ⅲ.openGauss 并发管制
Ⅳ.openGauss 分布式事务
openGauss 数据库安全
openGauss 事务机制
openGauss 事务 ACID 个性介绍
openGauss 中的事务一致性 03
在上一节的图 3 分布式事务一致性的例子中,对于并发执行的事务,如果没有一种机制来保障,那么其中的读事务,可能会只读到并发写事务的局部数据。事实上,对于并发的单机事务,也可能存在相似的景象。
仍思考第一节中的例子,只是插入事务 T1 和查问事务 T2 都产生在同一个 DN 上。如图 6 所示,首先 T1 在表 t 中插入 v1 和 v2 两条记录,在其提交之前,查问事务 T2 开始执行。在 T2 程序扫描表 t 的过程中,首先扫描到 v1 记录,然而因为此时 v1 记录的 xmin 对应的 XID1(T1 的事务号)还没有提交,因而 v1 不可见。而后 T1 实现提交,T2 持续扫描,并扫描到 v2 记录,此时 v2 记录的 xmin 对应的 XID1 曾经提交,因而 v2 可见。这样,查问事务 T2 只看到了 T1 的局部插入数据,毁坏了事务的一致性要求。
图 6 单机事务一致性问题示意图
为了解决下面这个问题,openGauss 采纳多版本并发管制(MVCC)来保障与写事务并发执行的查问事务的一致性。
MVCC 的根本机制是:写事务不会原地批改元组内容,而是将被批改的元组标记为这条记录的一个旧版本(标记 xmax),同时插入一条批改后的元组,从而产生这条记录的一个新版本;对于在一个查问事务开始时还没有提交的写事务,那么这个查问事务始终认为该写事务没有提交。
在下面的例子中,在 T2 开始的时候,T1 还没有提交,那么对于 T2 扫描上来的 v1 和 v2 记录,T2 会认为它们 xmin 对应的 XID1 均为未提交的,即这两个新版本对于 T1 均不可见,因而不会返回任何一条记录,也就不会产生读到局部事务内容的异常情况了。
在 MVCC 中,最要害的技术点有两个:①元组版本号的实现;②快照的实现。上面具体阐明这两个技术点在 openGauss 中的实现。
在 openGauss 中,采纳全局递增的事务号来作为一个元组的版本号,每个写事务都会取得一个新的事务号。如上所述,一个元组的头部会记录两个事务号 xmin 和 xmax,别离对应元组的插入事务和删除(更新)事务。xmin 和 xmax 决定了元组的生命期,亦即该版本的可见性窗口。
相比之下,快照的实现要更为简单。在 openGauss 中,有两种形式来实现快照。
§ 办法一:沉闷事务数组法。
在数据库过程中,保护一个全局的数组,其中的成员为正在执行的事务信息,包含事务的事务号,该数组即沉闷事务数组。在每个事务开始的时候,拷贝一份该数组内容。当事务执行过程中扫描到某个元组时,须要通过判断元组 xmin 和 xmax 这两个事务(即元组的插入事务和删除事务)对于查问事务的可见性,来决定该元组是否对查问事务可见。以 xmin 为例,首先查问 CLOG,判断该事务是否提交,如果未提交,则不可见;如果提交,则进一步判断该 xmin 是否在查问事务的沉闷事务数组中。如果 xmin 在该数组中,或者 xmin 的值大于该数组中事务号的最大值(事务号是全局递增发放的),那么该 xmin 事务肯定在该查问事务开始之后才会提交,因而对于查问事务不可见;如果 xmin 不在该数组中,或者小于该数组中事务号的最小值,那么该 xmin 事务肯定在该查问事务开始之前就曾经提交,因而对于查问事务可见。上述判断逻辑如图 7 所示。
图 7 基于沉闷事务数组办法的事务可见性判断示意图
元组 xmax 事务对于查问事务的可见性判断相似。最终,xmin(元组的插入事务事务号)和 xmax(元组的删除事务事务号)的不同组合,决定了该元组是否对于查问事务可见,如表 1 所示。
表 1 事务可见性判断
§ 计划二:工夫戳办法
应用沉闷事务数组办法,因为该数组个别比拟大,无奈应用原子操作,因而在其上的读 - 写并发操作须要加锁互斥,写 - 写并发操作亦须要加锁互斥。其中,读操作是指事务开始时拷贝数组内容获取快照的操作,写操作是指事务开始时将事务信息退出到该数组中以及事务完结时将事务信息从该数组中移除的操作。在高并发的场景下,沉闷事务数组会成为加锁的热点和性能瓶颈。
获取快照,实质上是要获取事务运行状态与工夫的映射关系 f(t)。对每一个事务来说,该 f(t) 函数为一个阶梯函数,如图 8 所示,在该事务的提交时刻点 tcommit 之前,f(t) 为未提交状态,在 tcommit 之后,f(t) 为提交状态。
图 8 事务运行状态与工夫函数关系的示意图
由此,某一个事务 T 的快照内容,即是其它所有事务 Tother 的事务状态函数 fother(t) 在该事务开始时刻点 tstart 的取值状态。依据 fother 的定义,可知,若 tstart <=,则该事务 Tother 在 T 的快照中为未提交状态,其对数据库的写操作对事务 T 不可见;若 tstart > ,则该事务 Tother 在 T 的快照中为提交状态,其对数据库的写操作对事务 T 可见。
在 openGauss 外部,应用一个全局自增的长整数来作为逻辑的工夫戳,模仿数据库外部的时序,该逻辑工夫戳被称为提交顺序号(Commit Sequence Number,简称 CSN)。每当一个事务提交的时候,在提交顺序号日志中(Commit Sequence Number Log,简称 CSN 日志)会记录该事务事务号 XID(事务的全局惟一标识)对应的逻辑工夫戳 CSN 值。CSN 日志中记录的 XID 值与 CSN 值的对应关系,即决定了所有事务的状态函数 f(t)。
如图 9 所示,在一个事务的理论执行过程中,并不会在一开始就加载全量的 CSN 日志,而是在扫描到某条记录当前,才会去 CSN 日志中查问该条记录头部 xmin 和 xmax 这两个事务号对应的 CSN 值,并基于此进行可见性判断。
图 9 基于工夫戳办法的事务可见性判断示意图
openGauss 中的事务隔离性 04
在上大节中,事务的一致性反映的是某一个事务在其它并发事务“眼中”的状态。本大节要介绍事务的隔离性,是某一个事务执行过程中,它“眼中”其它所有并发事务的状态。一致性和隔离性,两者互相分割,在 openGauss 中均是基于 MVCC 和快照实现的;同时,两者又有肯定区别,对于较高的隔离级别,除了 MVCC 和快照之外,还须要辅以其它的机制来实现。
如表 2 所示,在数据库业界,个别将隔离性按由低到高分为以下四个隔离级别,每个隔离级别依照在该级别下禁止产生的异常现象来定义。这些异常现象包含:
§ 脏读,指一个事务在执行过程中读到并发的、还没有提交的写事务的批改内容。
§ 不可反复读,指在同一个事务内,先后两次读到的同一条记录的内容产生了变动(被并发的写事务批改)。
§ 幻读,指在同一个事务内,先后两次执行的、谓词条件雷同的范畴查问,返回的后果不同(并发写事务插入了新的记录)。
隔离级别越高,在一个事务执行过程中,它能“感知”到的并发事务的影响越小。在最高的可串行化隔离级别下,任意一个事务的执行,均“感知”不到有任何其它并发事务执行的影响,并且所有事务执行的成果就和一个挨一个程序执行的成果完全相同。
表 2 事务隔离级别
在 openGauss 中,隔离级别的实现基于 MVCC 和快照机制,因而这种隔离形式被称为快照隔离(Snapshot Isolation,简称 SI)。目前,openGauss 反对读已提交(Read Committed)和可反复读(Repeatable Read)这两种隔离级别。两者实现上的差异在于在一个事务中获取快照的次数。
如果采纳读已提交的隔离级别,那么在一个事务块中每条语句的执行开始阶段,都会去获取一次最新的快照,从而能够看到那些在本事务块开始当前、在后面语句执行过程中提交的并发事务的成果。如果采纳可反复读的隔离级别,那么在一个事务块中,只会在第一条语句的执行开始阶段,获取一次快照,前面执行的所有语句都会采纳这个快照,整个事务块中的所有语句均不会看到该快照之后提交的并发事务的成果。
咱们通过具体的例子来阐明一下读已提交和可反复读的区别。
思考以下三个并发执行的事务(表 t 蕴含一个整型字段 a):
T1:
START TRANSACTION;
INSERT INTO t VALUES (v1);
COMMIT;
T2:
START TRANSACTION;
INSERT INTO t VALUES (v2);
COMMIT;
T3:
START TRANSACTION;
SELECT * FROM t;
SELECT * FROM t;
SELECT * FROM t;
COMMIT;
这三个事务的并发执行程序如图 10 所示。咱们思考 T3 事务三条查问的返回后果。如果采纳读已提交的隔离级别,那么在第一条查问开始时,首次获取快照,T1 和 T2 均没有提交,因而它们都在快照中,查问后果不会蕴含它们插入的新记录;在第二条查问开始时,第二次获取快照,T1 曾经提交,在第二条查问语句的快照中,只有 T2,因而能够查问到 T 1 插入的记录 v1;同理,在第三条查问开始时,第三次获取快照,T1 和 T2 均曾经提交,它们都不在第三条语句的快照中,因而能够查问到它们插入的记录 v1 和 v2。
另一方面,如果采纳可反复读的隔离级别,对于 T3 中的三条查问语句,均会采纳第一条语句执行开始时的快照,而 T1 和 T2 均在该快照中,因而在该隔离级别下,T3 的三条查问语句均不会返回 v1 和 v2。
图 10 读已提交和可反复读隔离级别在并发事务下的体现区别
未完待续 ……