共计 4045 个字符,预计需要花费 11 分钟才能阅读完成。
目录
openGauss 数据库 SQL 引擎
openGauss 数据库执行器技术
openGauss 存储技术
一、openGauss 存储概览
二、openGauss 行存储引擎
三、openGauss 列存储引擎
四、openGauss 内存引擎
Ⅰ. 内存引擎的兼容性设计
Ⅱ. 内存引擎索引
Ⅲ. 内存引擎的并发管制
Ⅳ. 内存引擎的内存管控
Ⅴ. 内存引擎的长久化
openGauss 事务机制
openGauss 数据库安全
openGauss 存储技术
四.openGauss 内存引擎
内存引擎的并发管制 03
内存引擎的并发管制机制采纳 OCC,在操作数据抵触少的场景下,并发性能很好。
内存引擎的事务周期以及并发管控组件构造,如图 42 所示。
图 42 内存引擎的事务周期以及并发管控组件构造
这里须要解释一下,内存引擎的数据组织为什么整体是一个靠近无锁化的设计。
除去以上提到的 Masstree 自身的无锁化机制外,内存引擎的流程机制也进一步最小化了并发抵触的存在。
每个工作线程会将事务处理过程中所有须要读取的记录,复制一份至本地内存,保留在 read-set(读数据集)中,并在事务全程基于这些本地数据进行相应计算。相应的运算后果保留在工作线程本地的 write set(写数据集)中。直至事务运行结束,工作线程会进入尝试提交流程,对 read set(读数据集)和 write set 进行 validate(查看验证)操作并在容许的状况下对 write set 中数据对应的全局版本进行更新。
这样的流程,会把事务流程中对于全局版本的影响,放大到 validation 的过程,而在事务进行其余任何操作的过程中都不会影响到其余的并发事务。并且,在仅有的 validation(查看验证)过程中,所须要的也并不是传统意义上的锁,而仅是记录头部信息中的代表锁的数位(lock bit)。相应的这些思考,都是为了最小化并发中可能呈现的资源争抢以及抵触,并更无效地应用 CPU 缓存。
同时 read set(读数据集)和 write set(写数据集)的存在,能够良好地反对各个隔离级别,不同隔离级别能够通过在 validation(查看验证)阶段对 read set(读数据集)和 write set(写数据集)进行不同的审查机制来取得。通过查看两个 set(数据集)中行记录在全局版本中对应的 lock bit(锁定位)以及行头中的 TID 构造,能够判断本人的读、写与其余事务的抵触状况,进而判断本人在不同隔离级别下是否能够 commit(提交)、或是须要 abort(终止)。同时因为 Masstree 中 Trie node 中存在版本记录,Masstree 的结构性改变(insert/delete,插入 / 删除)会更改相干 Trie node(节点)下面的版本号。因而保护一个 Range query(范畴查问)波及的 node set(节点集),并在 validation(查看验证)阶段对其进行比照校验,能够比拟容易地在事务提交阶段查看此 Range query 所波及的子集是否有过变动,从而可能检测到 Phantom(幻读)的存在,是一个工夫复杂度很低的操作。
内存引擎的内存管控 04
因为内存引擎的数据是全内存态的,因而能够依照记录来组织数据,不须要听从页面的数据组织模式,从而从数据操作的抵触粒度这一点上有着很大劣势。解脱了段页式的限度,不再须要共享缓存区进行缓存以及与磁盘间的交互淘汰,设计上不须要思考 IO 以及磁盘性能的优化(比方索引 B + 树的高度以及 HDD(Hard Disk Driv,磁盘)对应的随机读写问题),数据读取和运算就能够进行大量的优化和并发改进。
因为是全内存的数据状态,内存资源的管控就显得尤为重要,内存分配机制及实现会很大水平上影响到内存引擎的计算吞吐能力。内存引擎的内存治理次要分为 3 层,如图 43 所示。
图 43 内存引擎的内存治理示意图
上面别离对 3 层设计进行介绍:
(1) 第一层为内存引擎本身,蕴含了长期的内存应用以及长期的内存应用(数据存储)。
(2) 第二层为对象的内存池,次要负责为第一层对象如表、索引、行记录、Key 值、以及 Sentinel(行指针)提供内存。该层从底层索取大块内存,再进行细粒度的调配。
(3) 第三层为资源管理层,次要负责与操作系统之间的交互,以及理论的内存申请。为升高内存申请的调用开销,交互单位个别在 2 MB 左右。此层同时也有内存预取和预占用的性能。
第三层实际上是十分重要的,次要因为:
(1) 内存预取能够十分无效的升高内存调配开销,进步吞吐量。
(2) 与 NUMA 库进行交互的性能老本十分高,如果间接放在交互层会对性能产生很大影响。
内存引擎对短期与长期的内存应用针对 NUMA 构造适配的角度也是不同的。短期应用,个别为事务或 session(会话)自身,那么此时个别须要在解决该 session 的 CPU 核对应的 NUMA 节点上获取本地内存,使得 transaction(交易)自身的内存应用有着较小的开销;而长期的内存应用,如表、索引、记录的存储,则须要 NUMA 概念中 interleaved 内存,并且要尽量平均分配在各个 NUMA 节点上,来避免单个 NUMA 节点内存耗费过多带来的性能降落。
短期的内存应用,也就是 NUMA 角度的本地内存,也有一个很重要的个性,就是这部分内存仅供本事务本身应用(比方复制的读取数据以及做出的更新数据),因而也就防止了这部分内存上的并发管控。
内存引擎的长久化 05
内存引擎基于同步的 WAL 机制以及 Checkpoint(检查点)来保证数据的长久化,并且此处通过兼容 openGauss 的 WAL 机制(即 Transaction log,事务日志),在数据长久化的同时,也能够保证数据可能在主备节点之间进行同步,从而提供 RPO= 0 的高牢靠以及较小 RTO 的高可用能力。
内存引擎的长久化机制如图 44 所示。
图 44 内存引擎的长久化机制
能够看到,openGauss 的 Xlog 模块被内存引擎对应的 manager(管理器)所调用,长久化日志通过 WAL 的 writer 线程(刷新磁盘线程)写至磁盘,同时被 wal_sender(事务日志发送线程)调起发往备机,并在备机 wal_receiver(事务日志接管线程)处接管、落盘与复原。
内存引擎的 Checkpoint 也是依据 openGauss 本身的 Checkpointer 机制被调起。
openGauss 中的 Checkpoint 机制是通过在做 Checkpoint 时进行 shared_buffer(共享缓冲区)中脏页的刷盘,以及一条非凡 checkpoint 日志来实现的。内存引擎因为是全内存存储,没有脏页的概念,因而实现了基于 CALC 的 Checkpoint 机制。
这里次要波及一个局部多版本(partial multi-versioning)的概念:当一个 Checkpoint 指令被下发,应用两个版本来追踪一个记录:沉闷(live)版本,也就是该记录的最新版本;稳固(stable)版本,也就是在 Checkpoint 被下发、造成虚构一致性点时此记录对应的版本。在一致性点之前提交的事务须要更新沉闷(live)和稳固(stable)两个版本,而在一致性点之后的事务仅更新沉闷(live)版本本放弃 stable 版本不变。在无 Checkpoint 状态的时候,实际上稳固(stable)版本是空的,代表着 stable 与 live 版本在此时理论是雷同的值;仅有在 Checkpoint 过程中,在一致性点后有事务对记录进行更新,此时才会须要依据双版本来保障 checkpoint 与其余失常事务流程的并行运作。
CALC(Checkpointing Asynchronously using Logical Consistency,逻辑一致性异步检查点)的实现有 5 个阶段:
(1) rest(劳动)阶段:这个阶段内,没有 Checkpoint(检查点)的流程,每个记录仅存储 live 版本。
(2) prepare(筹备)阶段:整个零碎触发 Checkpoint 后,会马上进入这个阶段。在这个阶段中事务对读写的更改,也会更新 live 版本;然而在更新前,如果 stable 版本不存在,那么在更新 live 版本前,live 版本的数据会被存入 stable 版本。在此事务的更新完结,在放锁前,会进行查看:如果此时零碎依然处于 prepare 阶段,那么刚刚生成的 stable 版本能够被移除;反之,如果整个零碎曾经脱离 prepare 阶段进入下一阶段,那么 stable 版本就会被保留下来。
(3) resolve(解析)阶段:在进入 prepare 阶段前产生的所有事务都已提交或回滚后,零碎就会进入 resolve 阶段,进入这个阶段也就代表着一个虚构一致性点曾经产生,在此阶段前提交的事务相干的改变都会被反映到此次 Checkpoint 中。
(4) capture(捕捉)阶段:在 prepare 阶段所有事务都完结后,零碎就会进入 capture 阶段。此时后盾线程会开始将 Checkpoint 对应的版本(如果没有 stable 版本的记录即则为 live 版本)写入磁盘,并删除 stable 版本。
(5) complete(实现)阶段:在 Checkpoint 写入过程完结后,并且 capture 阶段中进行的所有事务都完结后,零碎进入 complete 阶段,零碎事务的写操作的体现会复原和 rest 阶段雷同的默认状态。
CALC 有着以下长处:
(1) 低内存耗费:每个记录至少在 Checkpoint 时造成两份数据。在 Checkpoint 进行中如果该记录 stable 版本和 live 版本雷同,或在没有 checkpoint 的状况下,内存中只会有数据本身的物理存储。
(2) 较低的实现代价:绝对其余内存库 Checkpoint 机制,对整个零碎的影响较小。
(3) 应用虚构一致性点:不须要阻断整个数据库的业务以及解决流程来达到一份物理一致性点,而是通过局部多版本来达到一个虚构一致性点。
至此,“openGauss 存储技术”章节全副完结,下一篇文章将开始对“openGauss 事务机制”章节进行解说,敬请期待 ……