openGauss源码解析(53)

openGauss源码解析:存储引擎源码解析(21)

2. 故障恢复机制

当数据库发生宕机重启之后需要从检查点位置开始回放之后所有的日志。不同类型的日志的回放逻辑由对应的资源管理器来实现。

当用户业务压力较大时会同时有很多业务线程并发执行事务和日志记录的插入,单位时间内产生的日志量是非常大的。对此openGauss采用多种回放线程组来进行日志的并行回放,各个回放线程组之间采用高效的流水线工作方式,各个回放线程组内采用多线程并行的工作方式,以便保证日志的回放速率不会明显低于日志产生的速率。

图4-35 openGauss并行回放流程示意图

openGauss并行回放流程如图4-35所示,其中每个线程(组)的运行机制如下。

(1) “Walreceiver”线程收到日志成功写入磁盘后,“XLogReadWorker”线程从“Walreceiver”线程的缓冲区中读取字节流,“XLogReadManager”线程将字节流decode(解码)成redoitem(单个回放对象)。“Startupxlog”线程按照表文件名粒度(refilenode)将redoitem发放给各个“ParseRedoRecord”线程,其他的日志发送给“TrxnManager”线程。
(2) “ParseRedoRecord”线程负责表文件(relation)相关的日志处理,从队列中获取批量的日志进行解析,将日志按照页面粒度进行拆分,然后发给“PageRedoManager”线程。拆分原理如下。

➀ 针对行存储表、索引等数据页面操作的日志,按照涉及的页面个数拆成多条日志。例如heap_update日志,如果删除的老元组和插入的新元组在不同的页面上,那么会被拆成2条,分别插入到哈希表中。

➁ xact、truncate、drop database等日志是针对表的,不能进行拆分。针对这些日志,先清理掉哈希表中相关日志,然后等这些日志之前的日志都回放之后,再在PageRedoManger中进行回放,并将该日志分发给所有“PageRedoWorker”线程来进行invalid page(无效页面)的清理、数据写入磁盘等操作。

➂ 针对Createdb(创建数据库)操作要等所有“PageRedoWorker”线程将Createdb日志之前的日志都回放后,再由一个“PageRedoManager”线程进行Createdb操作的回放。这个过程中其余线程需要等待Createdb操作回放结束后才能继续回放后续日志。

(3) “PageRedoManager”线程利用哈希表按照页面粒度组织日志,同一个页面的日志按照lsn顺序放入到一个列表中,之后将页面日志列表分发给“PageRedoWorker”线程。
(4) “PageRedoWorker”线程负责页面日志回放功能,从队列中获取一个日志列表进行批量处理。
(5) “TrxnManager”线程负责事务相关的XLOG日志的分发,以及需要全局协调的事务处理。
(6) “TrxnWorker”线程负责事务日志回放功能,从队列中获取一个日志进行处理。当前只有一个“TrxnWorker”线程负责处理事务日志。

为了保证高效的日志分发性能,“PageRedoManager”进程和“PageRedoWorker”进程之间采用了带阻塞功能的无锁单生产者单消费者(single producer single consumer,SPSC)队列。如图4-36所示,分配线程作为生产者将解析后的日志放入回放线程的列队中,回放线程从队列中消费日志进行回放。另一方面为了提升整体并行回放机制的可靠性,会在一个页面的回放动作中对日志记录头部的lsn和页面头部的lsn进行校验,以保证回放过程中数据库系统的一致性。

D:\工作流水\202004\20200402手册第十章内容替换英文\原稿\已修改图\图10-24  无锁SPSC队列.png

图4-36 无锁SPSC队列示意图

3. cstore列存储持久化机制

由于在openGauss中cstore主体数据没有写缓冲区,因此对于所有的插入或更新事务,在拼装完新的CU之后都是直接调用pwrite来写入文件系统缓存,并且在事务提交之前调用“CUStorage::FlushDataFile”接口完成本地磁盘的持久化(该函数内部调用fsync执行写入磁盘)。由于OLAP系统中通常插入事务都是批量导入执行的,因此在这个过程中对于cstore表物理文件的写操作基本都是顺序I/O,可以获得较高的性能。

posted @ 2024-04-29 16:22  openGauss-bot  阅读(8)  评论(0编辑  收藏  举报