Vastbase G100核心技术介绍之【并行回放】

2022-02-18 00:00:00 并行 事务 线程 日志 回放

本文针对串行回放并行回放两种技术问题,比较开源PostgreSQL和Vastbase在实现临界区保护上的差异以及Vastbase为了提升性能而做的优化。


 串行回放的问题


在PostgreSQL中Startup子进程负责从磁盘读取XLog并回放,前一条日志没回放完成的情况下无法继续处理下一条XLog。虽然XLog在写入时是按照事务发生的先后顺序,但前后之间不一定存在依赖(例如向两张不同的表中插入数据的两个事务),不难发现若两条XLog之间不存在依赖时可以同时回放,但为了提供hot standby(备机可读)功能,各事务的提交日志需要按顺序回放。


此外,在主备集群场景下,备库负责回放的只有Startup子进程,无法充分利用服务器资源,若主备间开启了同步复制,备库回放速率会影响主库吞吐率;若采用异步复制,由于主备间事务处理的性能差异,会造成主库日志堆积。为了解决串行回放的问题,Vastbase引入了并行回放机制。


 并行回放逻辑架构


要实现并行回放,它需要解决以下问题:


1

造Startup线程,将回放工作进行拆分;

2

处理XLog之间存在依赖的情况。


为了解决个问题,Vastbase引入了PageRedoWorker线程,该线程负责回放非事务日志(例如表数据的插入和删除);日志之间的依赖存在两种情况:


1

两个事务之间存在依赖导致XLog必须按顺序回放,例如先创建表,然后插入数据,必须先回放创建表的日志;

2

同一个事务内的顺序依赖,例如事务提交日志必须等待前面的操作回放完成。


为了简化问题,Vastbase并行回放根据日志类型选择不同线程来进行回放,备库并行回放逻辑架构图如下:



其中WalRcvWriter线程负责接受主库发送的日志并落盘,SPSC QUEUE是一个单生产者单消费者队列,用于存储Startup线程分发的XLog。


并行回放过程


并行日志回放过程如下:


1

WalRcvWriter将主库发送的XLog写入磁盘;

2

Startup子线程从磁盘读取XLog,根据XLog的类型调用不同的分发(dispatch)函数,其中:

A、非事务日志分发给PageRedoWorker线程:根据处理的数据文件选择对应的回放线程;

B、事务日志由Startup线程自身回放;分发的过程就是将XLog放入对应PageRedoWorker的SPSC队列中,若队列满了Startup线程会循环等待。

3

PageRedoWorker线程从SPSC队列中读取XLog后回放,更新本线程已经回放的XLog位置;

4

Startup线程获取所有PageRedoWorker中已经回放的XLog的小位置,判断自身的事务日志能否回放,若可以则调用对应的回放函数;


通过保证只有一个线程(Startup)来回放事务日志避免了事务回放乱序的问题,但还有个问题:即使是同一个事务内也可能存在对同一张表多次操作的XLog(例如先做INSERT,再做DELETE),这些操作之间也可能存在依赖,为了解决这个问题,并行回放会根据表的relfilenode计算回放此日志的工作线程编号。除非对表做修改(例如truncate),不然表的relfilenode是不会发生变化,这样保证同一个表的事务日志都由同一个线程回放,也就保证了回放的先后顺序,但这带来了另外一个问题:若某张表上的操作非常频繁(例如Benchmark TPC-C场景),导致产生的XLog非常多,会使得某个工作线程非常繁忙,剩下的工作线程可能很空闲。


除此之外,还有一些特殊的DDL操作,例如创建表空间、创建数据库等,需要所有工作线程之间同步,并行回放机制回放的并不是“原始”的XLog,而是对其进行了封装,通过一些状态来标记待回放的日志是否需要同步。


并行回放结果处理


在PostgreSQL中回放操作都由Startup进程负责,当XLog回放完成(例如故障恢复场景)或收到备升主请求时回放结束,完成清理动作后终Startup进程会退出。Vastbase中的并行回放除了Startup子线程外还有若干PageRedoWorker线程,并行回放结束阶段的逻辑稍微复杂点,需要通知参与回放的线程进行清理并退出。


回放结束时,Startup线程向所有回放工作线程发送一个“伪”XLog,回放工作线程从SPSC队列中读取到该XLog就知道回放已经结束,接下来会执行回放清理,然后设置信号量告知Startup线程自身已经处理完成。


Vastbase针对并行回放的改进与进步


通过对并行回放机制的分析不难发现存在以下问题:


1

Startup线程做的事情太多了:既要负责读取XLog,还要负责分发,还要负责回放事务日志;

2

若某张大表上的操作非常频繁,会导致某个回放工作线程非常繁忙,而其他工作线程可能很空闲;

3

Startup从磁盘读取XLog日志,IO代价过高。虽然WAL要求日志先落盘,但可以通过其他方式来保证,降低读取XLog的代价。



针对问题1,可以将Startup的现有工作拆分为两部分:读取XLog并分发、回放事务日志,Startup线程仅负责读取XLog并分发,创建新的线程负责事务日志回放;


针对问题2,可以将每个数据文件的回放进一步拆分,由多个线程回放,根据数据块编号来选择回放线程,通过增加并行度来提升回放的效率;


针对问题3,可以在XLog落盘后从WAL接收缓冲区中读取,避免从磁盘读取。



原文链接:https://mp.weixin.qq.com/s/gdnN_SANA3DkDwob1lQyLA



上述优化措施正是Vastbase中RTO机制针对并行回放中存在的问题做出的改进。

相关文章