共计 3819 个字符,预计需要花费 10 分钟才能阅读完成。
目录
openGauss 数据库 SQL 引擎
openGauss 数据库执行器技术
openGauss 存储技术
一、openGa uss 存储概览
二、openGauss 行存储引擎
Ⅰ、行存储引擎总体架构
Ⅱ、行存储的根本模型与页面组织构造
Ⅲ、行存储的多版本治理以及 DML 操作
Ⅳ、基于 CSN 的 MVCC 机制
Ⅴ、行存储的空间回收
Ⅵ、行存储的共享缓存治理
Ⅶ、并行日志零碎设计
Ⅷ、长久化及故障复原零碎设计
三、openGauss 列存储引擎
四、openGauss 内存引擎
openGauss 事务机制
openGauss 数据库安全
openGauss 存储技术
二.openGauss 行存储引擎
行存储的多版本治理以及 DML 操作 03
openGauss 行存储的多版本机制与业界比拟常见的关系型数据库有较大的不同,外围区别为行存储的多版本在更新的时候并不是就地更新,而是在原有页面中保留上一个版本,转而在这个页面(如果空间不够会在新页面中)创立一个新的版本,来进行历史版本的累积更新。
相应的页面中会同时存有不同版本的同一行数据,拿到不同快照的事务,在读写这些不同版本时互不抵触,有着很好的并发性能。对历史版本的检索能够在页面自身或邻近页面进行,也不须要额定的 CPU 开销以及 IO 开销,有着十分高的效率。同时,事务管理以及长久化角度也变得十分的清晰简洁,省去了相似于就地更新所须要记录、执行以及长久化的 Undo 等相干操作。
以下就以一个 DML 的例子简略开展行存储构造以及 MVCC 的实现:
假如咱们在一个 Xid 为 10 的事务中,在一个只有一列 varchar(变长字符串(类型))数据的表中插入一条数据’A’,该行数据落入编号为 0 的数据页面上,则该行构造如图 5 所示。
图 5 行存储构造示意图 1
能够看到 xmax 为 0,此时此记录为无效记录。
假如咱们在此基础上在事务 xid=20 做了 delete 此行的操作,则此记录变为如图 6 所示。
图 6 行存储构造示意图 2
此时 xmax 被标记为 20,如果此事务提交,那么此行最终会被回收。
如果咱们在之前 insert 的根底上,在事务 xid=30 中间断对该行做两次更新,则改行记录则会如下所示:
第一次更新如图 7 所示。
图 7 行存储构造示意图 3
原有行生效,通过 ctid 记录新版本的 ctid,进而指向下一行
第二次更新如图 8 所示。
图 8 行存储构造示意图 4
第二个版本也变为历史版本,通过 ctid 指向最新版本,不过值得注意的是,第二个版本的 xmin、xmax 都为 30,即此版本在同一事务中被删除,而最新版本 xmin 也仍为 30,只是 cid 从 0 减少为 1(假如此事务间断执行了这两次 Update 操作)。
更新后的页面如图 9 所示。
图 9 行存储构造示意图 5
以上几个简略的例子比拟直白的展现了行存储的根本存储构造、行存储的 DML 以及行存储的 MVCC 是如何联合在一起独特作用的。
存储引擎外部,索引也是重要的组成部分,索引自身指向存储的是 key 到 ctid 的映射。下面咱们也提到过了,ctid 实际上指向的是 line_pointer 的检索信息,因而索引的页面上存储的信息以及其与数据页面的关系如图 10 所示。
图 10 索引的页面上存储的信息以及其与数据页面的关系
当然,可能会呈现更新操作的新版本无奈放入旧版本所在页面的状况,这种状况下页面和索引状况的对比方图 11 所示。
图 11 新版本无奈放入旧版本所在页面时的页面和索引状况
此种状况下,Index 会有两条 entry(记录),两条 entry(记录)代表了 key(键)对应新旧版本的 ctid,这样不便从索引间接跨页面进行搜寻。
基于 CSN 的 MVCC 机制 04
openGauss 采纳行级 MVCC 机制,历史版本集中存储,垃圾清理代价低。每个事务有一个独自的事务状态存储区域,记录了该事务的状态信息和 CSN(Commit Sequence Number,提交顺序号)。CSN 在 openGauss 外部应用一个全局自增的长整数来作为逻辑的工夫戳,模仿数据库外部的时序。
举例来说:如图 12 所示,图中每个非只读事务在运行过程中会获得一个 XID(事务号),在事务提交时会推动 CSN,同时会将以后 CSN 与事务的 XID 映射关系保存起来。
图 12 CSN-XID 映射
因而当一个事务拿到的快照为 CSN= 3 时,事务 TX2、TX4、TX6、TX7、TX8 的 CSN 别离为 4、6、5、7、8,对于该事务的快照而言,这几个事务的批改都不可见。
MVCC 解决的是读写并发抵触问题。更新数据的时候,原地更新,把老版本放到历史版本区页面里,同时保护新版本 TUPLE(元组)到老 TUPLE(元组)的指针。读 TUPLE 的时候,依据快照 SNPAHOST.CSN 来判断应该读到哪个版本。
数据库在执行 SQL 的时候,首先会获取一个快照工夫戳 SNAPSHOT,当扫描数据页面的时候,依据 SNAPSHOT.CSN 和事务状态来判断哪个 TUPLE 版本可见或者都不可见。次要分以下 3 种场景:
(1) TUPLE(元组)的事务状态区里是回滚状态或者运行中,不可见。
(2) TUPLE(元组)的事务状态区里是提交状态,如果 SNAPSHOT.CSN 比事务区里的 CSN 小,以后 TUPLE 不可见,读取前一个版本持续比拟 CSN。反之可见。
(3) TUPLE(元组)的事务状态区里是待提交状态,须要期待提交。
CSN(Commit Sequence Number,提交顺序号)自身与 XID(事务号)也会留存一个映射关系,以便将事务自身以及其对应的可见性进行关联,这个映射关系会留存在 CSNLog 中,如图 13 所示。
图 13 CSNLog 中映射关系
此映射机制相似于 Clog 自身,只不过不同的是,Clog 记录的是事务 ID 的相干运行状态(运行中 / 提交 / 回滚),如图 14 所示。
图 14 Clog 记录的事务 ID 的相干运行状态
进一步联合后面讲过的行头的构造(其中的 xmin、xmax)以及 Clog、上述 CSNLOG 的映射机制,MVCC 的大抵判断流程如图 15 所示。
图 15 MVCC 判断流程
简略的总结来说:
§ 如果以后事务 ID 小于一行的 xmin,那么就须要检索 xmin 对应的 clog,读取此事务状态,以此来判断此行数据是否对以后事务可见。
§ 反之,如果以后事务 ID 大于一行中的 xmax,那么阐明此行数据的更新 / 删除产生于本事务开始之前,此行数据对本事务肯定不可见(但不排除此行数据的新版本对本事务可见,因为新旧版本是独自进行判断的)
§ 如果 XID 落在了 Xmin、Xmax 两头,就须要根据 CSN 来判断本事务的快照下,对应数据是否应该被看到,须要检索 CSNLog 来进行比照判断。
行存储的空间回收 05
通过上述章节所介绍的行存储的多版本管理机制,能够发现因为更新和删除并不理论在页面中删除页面自身,数据库长时间运行后,会有大量的历史版本残存在存储空间中,造成了空间的收缩。为了解决这一问题,存储引擎外部须要定期的对历史数据进行清理,以保障数据库的衰弱运行。
行存储对于存储空间的清理存在于多个层面、有多种形式。其中在页面一级的机制,成为 heap_page_prune。顾名思义,就是在页面外部进行空间的清理。这种清理模式可能比拟好的解决更新多版本带来的同一个数据记录关联的长长的历史版本重叠、标记删除的记录以及有效的记录。这种 pruning 的伎俩在对页面进行读取的过程中由页面的闲暇空间阈值触发,仅改变 heap 页面自身,不对索引页面进行改变。因而 heap_page_prune 是一种较为轻量化的清理形式。举例如下:
如有一个记录 a,被前后更新导致同时有 6 个历史版本,保留于两个不同的页面中,如图 16 所示。
图 16 记录 a 的 6 个历史版本
page 页面级别的自我清理成果为图 17 所示。
图 17 page 页面级别的自我清理
能够看到,清理过程中别离对 page1 和 page2 中的内容进行了回收,然而因为之前的跨页面导致的两个索引 entry 指向不同页面,却被保留了下来。
在页面级别的清理之外,还有表级别、数据库级别的整体清理,这个机制称之为 Vacuum 操作。Vacuum 操作在整个数据库级别进行废旧元组的清理,同时也会清理索引。Vacuum 操作能够由数据库用户对数据库或数据库内对象被动调起,同时数据库后盾也会有工作线程在满足阈值时或者定期进行数据库主动的 Vacuum。如图 18 所示。
图 18 Vacuum 操作
Vacuum 本身除了清理空间外,也顺带圣诞了更新统计信息的性能,以便优化器能更精确的进行代价估算。
Vacuum 在过程中,还会对整个数据库级别都可见的元组进行 freeze 操作。举例来说,当一个元组被插入并提交,而后续没有更新操作,数据库系统上也不再有早于这个提交的事务工夫点、须要对这条元组做可见性判断的事务,此时认为此元组就能够被任何人看见了,那么其相干的事务 ID 就能够被转化为一个非凡的事务 ID——Freeze Xid,以示意这种状态。当 Vacuum 清理整个零碎时,零碎中最小沉闷事务之前的提交日志(Clog),也同下面说到的,不在被须要,因而 Vacuum 操作也会对这部分 Clog 日志进行清理和回收。
当然,Vacuum 自身是一个绝对高老本的操作,因而,每个表文件会有一个对应的可见性映射(visibility map),来记录这个表数据文件中对应的页面是否曾经处于全副可见状态,这种状况下 Vacuum 在执行过程中就能够跳过这部分页面,节俭开销。因为个别零碎中存储的绝大部分数据都不与以后沉闷事务相干,因而此优化能够大大晋升 Vacuum 的效率。
未完待续 …….