共计 3750 个字符,预计需要花费 10 分钟才能阅读完成。
目录
openGauss 数据库 SQL 引擎
openGauss 数据库执行器技术
openGauss 存储技术
一、openGauss 存储概览
二、openGauss 行存储引擎
Ⅰ、行存储引擎总体架构
Ⅱ、行存储的根本模型与页面组织构造
Ⅲ、行存储的多版本治理以及 DML 操作
Ⅳ、基于 CSN 的 MVCC 机制
Ⅴ、行存储的空间回收
Ⅵ、行存储的共享缓存治理
Ⅶ、并行日志零碎设计
Ⅷ、长久化及故障复原零碎设计
三、openGauss 列存储引擎
四、openGauss 内存引擎
openGauss 事务机制
openGauss 数据库安全
openGauss 存储技术
二.openGauss 行存储引擎
行存储的共享缓存治理 06
后面提到,行存储是一个基于磁盘的存储引擎。为了防止 IO 的昂扬开销,存储引擎会缓存一部分页面在内存中,便于随时对其进行检索和更改。存储引擎会对缓存的页面进行筛选、替换和淘汰,保障留存在缓存的页面可能进步整个引擎的执行效率。
行存储中也有着品种较多的缓存,除去失常数据页面的缓存之外,还存在用于缓存各类表的元信息的 Relation Cache(数据表缓存),以及用于减速数据库系统信息以及零碎表操作的 Catalog Cache(零碎表缓存)。这些品种的缓存都以 page(页面)的模式归由共享缓冲区构造治理。
共享缓冲区由大量的页面槽位形成,槽位自身有对应的形容构造体,以及用于治理处于这个操作的并发操作的页面级别锁,并配有一个闲暇链表来进行闲暇空间治理。如图 19 所示。
图 19 共享缓冲区
行存储引擎中操作对事务的读写申请,都会被先传递至共享缓冲区。对一个页面的申请会当初缓冲区内进行搜寻,如果未命中,则获取一个空的槽位(可能须要淘汰掉曾经在缓冲区中不罕用的页面),再与文件系统进行交互将所需页面读到槽位中,加锁并应用。依据业务的特色和负载、以及共享缓冲区的大小,曾经在缓冲区内的数据页面会被重复命中,防止了与磁盘的 IO 开销,从而减速整个事务处理流程。
对页面的更改也会放在缓存中并被标为脏页面。此时后盾写线程(background writer)会定期对脏页面进行清理和刷盘操作,把空间返还给缓冲区。另一方面,Checkpoint,也就是检查点操作,在进行时也会将所有的页面刷盘,确保数据的长久化。这里须要留神的一个概念是,当一个事务提交后,这个事务执行过程中更改的页面并不一定被刷盘至磁盘,事务自身的长久化机制实际上是由事务强制刷盘的 WAL,也就是 xlog,来保障的。在 Checkpoint 操作后,因为相干页面都曾经长久化至磁盘,因而 Checkpoint 工夫点之前的 xlog,就能够被回收了。这个机制会在后续的章节持续开展。
共享缓冲区实际上是内存与长久化存储中协调治理调度的外围机制,对数据库管理系统的效率有着很大的影响。为了进一步晋升缓冲区中页面的命中率,一些可能会影响缓冲区内页面与业务关联性的操作,都会应用一个专门独自开拓的缓冲区,Ring Buffer(环状缓冲区)。批量的读、批量的写、以及 Vacuum 的页面清理,都属于这类操作。
并行日志零碎设计 07
数据库的日志零碎十分要害,它是数据长久化的要害保障。传统数据库个别都采纳串行刷日志的设计,因为日志有程序依赖关系,例如:一个是事务产生的 REDO/UNDO 日志是有前后依赖关系的。openGauss 的日志零碎采纳多个 LOG WRITER(日志写盘线程)线程并行写的机制,充分发挥 SSD 的多通道 IO 能力。如图 20 所示。
图 20 并行刷日志示意图
要害设计:
§ 整个事务的 wal 日志不能拆分到多个事务日志共享缓冲区,必须写到一个事务日志共享缓冲区。
§ 故障复原 wal,并行复原,必须依照 LSN 大小程序复原。
§ 每个事务完结前须要保障对应的事务日志 LSN 曾经刷盘实现。
§ 事务调配事务日志共享缓冲区思考 NUMA 架构适配。
长久化及故障复原零碎设计 08
数据库的日志零碎十分要害,它是数据长久化的要害保障。以其基于事务 ID 的多版本治理以及历史版本的累积及清理形式为根底,行存储引擎次要以 Redo 日志(也就是上文提到的 XLOG)作为次要的长久化伎俩,配以增量的检查点(Checkpoint)以及日志的并行回放,反对数据库实例的疾速故障复原。
- 事务的 Redo 日志机制
Redo 日志在事务对数据进行批改时产生,用来记录事务批改后的数据、或是事务对数据做的具体操作。比方,简略的 INSERT/UPDATE/DELETE 操作会产生如图 21 所示的 Redo 日志。
图 21 Redo 日志
一些非事务间接批改的要害操作也会记录到 Redo 日志,比方申请新的页面、显式的事务提交、检查点(Checkpoint)等。记录 Redo 日志的准则,就是在数据库产生故障后,能够从最初一个检查点开始,通过 Redo 日志的回放,复原到与数据库实例故障前的统一状态。
Redo 日志除了利用于数据恢复,数据的备份、还原以及数据库主备实例之间的主备同步、不同数据库实例 / 集群间的同步都须要依赖 Redo 日志的机制。为了保障数据的一致性,在事务批改的相干页面刷盘之前,须要先把对应的 Redo 日志刷盘,也就是 WAL(Write Ahead Log)的准则。
因为事务的提交以及操作之间的程序对于数据一致性是至关重要的,因而 Redo 日志也必须将此程序记录下来。每条 Redo 日志都配有一个日志编号,即 Log Sequence Number (LSN)。在行存储的零碎中,LSN 为一个递增的 64 位无符号整数。零碎中各类机制,如前面要说到的检查点,以及主备实例之间的同步机制、仲裁机制,都须要依附零碎中推动的 LSN 或是复原进去的 LSN 作为重要的标记或判断根据。
- 全量与增量检查点
在上述对事务日志以及共享缓冲区的形容中,有一个要害的信息,那就是事务日志的长久化与事务提交是同步的,但事务内对页面相干批改的长久化与事务提交不是同步的;也就是说,事务提交须要这个事物相干的 Redo 日志被强制刷盘,然而并不强制要求相干的页面也被强制刷盘。当一个数据库实例故障重启后,实例在启动过程中,之前没有可能及时刷盘的改变须要应用事务日志进行复原。然而日志回放的代价是很高的,性能也绝对比较慢。为了防止每次数据库都须要从头复原事务日志,数据库本身会定期创立检查点,用户也能够通过命令手动创立检查点。
创立检查点的过程中,存储引擎会将数据缓冲区中脏页写到磁盘中,并记录日志文件和管制文件。记录信息中 rec LSN 代表着此次查看点中,在此 LSN 之前的日志对应的所有改变均已被长久化,下次的数据恢复能够间接从此 LSN 开始;同时在此 LSN 之前的事务日志,在其余用处(主备实例同步、数据备份等)时,也能够被回收从新应用。
因为检查点自身须要将缓冲区内所有的脏页面刷盘(全量检查点),因而每次检查点从性能角度会对数据库实例所在物理环境引入大量的 IO,磁盘的峰值往往意味着性能的稳定。同时因为存在大量的 IO 开销,因而检查点的打点不能过于频繁,rec LSN 推动较慢,那么重启数据库时也就会存在较多的 Redo 日志须要回放,存在重启复原工夫过长的问题。为了解决这一问题,行存储引擎引入了增量检查点的概念。
增量检查点机制下,会保护一个脏页面队列(dirty page queue)。脏页是依照 LSN 递增的程序放到队列中的,定期由一个专门刷脏页面的后盾线程 pagewriter(页面刷盘线程)进行定期定量的刷脏页下盘操作。如图 22 所示。
图 22 脏页面队列
队列中保护一个 rec LSN,记录目前曾经被刷盘的脏页对应的 LSN 大小,即在队列中脏页对应的事务提交、其绝对的事务日志下盘后,此 rec LSN 标记会被更新。在触发增量检查点时,并不需要期待脏页刷盘,而是能够应用以后脏页队列的 rec LSN 作为检查点的 rec LSN 记录。增量 Checkpoint 的存在使得整个零碎中的 IO 更加平滑,并且零碎的故障复原工夫更短,可用性更高。
- 并行回放
Redo 日志的回放指的是将 Redo 日志中记录的改变从新利用到零碎 / 页面中的过程,这个过程通常产生在实例故障复原亦或是主备实例之间的数据同步过程中的备机实例上(即主实例的改变,备机实例也须要回放实现,以达到与主实例状态统一的成果)。以后数据库所在物理实例往往有较多的 CPU 核,而日志回放却往往还是单线程进行运作,在日志回放的过程中数据库实例无奈充分利用物理环境资源。
为了可能充分利用 CPU 多核的特点,显著放慢数据库异样后复原及备机实例日志回放的速度,行存储引擎采纳了多线程并行形式回放日志,如图 23 如示。
图 23 多线程并行形式回放日志
整个并行回放零碎的设计采纳生产者 - 消费者模型,调配模块负责解析、调配日志到回放模块,回放模块负责生产、回放日志。
为了达成这一设计,实现中采纳了带阻塞性能的无锁 SPSC(Single Producer Single Consumer)队列。调配线程作为生产者将解析后的日志放入回放线程的列队中,回放线程从队列中生产日志进行回放。如图 24 所示。
图 24 无锁 SPSC 队列
为了晋升整体并行回放机制的可靠性,会在对一个页面的回放动作中,对事务日志中的 LSN 和页面构造中的 last_LSN(详见后面章节中形容的 HeapPageHeader(堆页面头)构造体)进行校验,以保障回放过程中数据库系统的一致性。
未完待续 …….