《PolarDB for PostgreSQL源码与应用实战》——PolarDB for PostgreSQL高可用原理(上) https://developer.aliyun.com/article/1232684?groupCode=polardbforpg
一致性协议复制实现
(一)基本原理
接下来介绍一致性协议复制的具体实现。
首先我们看一下PolarDB for PG一致性协议复制的基本原理,主要包含两方面,第一方面是通过原生的异步流复制来传输WAL日志,另一方面是哪些日志在多数派上已经同步成功,这是由Leader节点上通过X-Paxos协议来确定的。对X-Paxos来说,日志提交位点是以Consensus Log为载体的,然后推进Consensus Log提交位点,所以我们会针对每一段WAL日志生成一个相应的Consensus Log etnry,然后Consensus Log etnry中记录的是这段WAL日志的一个结束LSN。
如上图的例子,比如在Leader节点上,我们已经有三段日志,有了相应的一个Consensus Log etnry,第一段LSN是从100-200,第二段是从200-300,第三段是从300-450,然后生成的Consensus Log Index分别是1、2和3,我们把ConsensusLog etnry和WAL日志位点首先要结合起来,然后我们又引入了持久化的依赖,就是说每个Log etnry在持久化的时候,必须要确保相应位点的WAL日志已经持久化成功。
LogIndex1要持久化之前,我们必须要确保LSN等于200之前的WAL日志都已经回刷了,这样Consensus Log的回刷位点本身跟WAL日志的回刷位点也结合起来了。
在引入了这两个机制之后,如果Consensus Log提交成功了,那么就是说Consensus Log肯定已经在多数派上持久化成功了。根据刚才的依赖,对应位点的WAL日志肯定也已经在多数派上持久化成功了。
大家可以看看下面的例子,Leader上面有三段WAL日志已经生成对应的Log etnry,然后三个Log etnry对应的WAL日志也都已经持久化了,包括Consensus Log etnry自己也都已经持久化了。那么在Follower1这个节点上,虽然Log index为3的WAL日志已经持久化成功了,但是可能由于并没有接收到Log Index为3的Consensus etnry,所以Log etnry并没有持久化成功。然后Follower2上面Log Index为3的Consensus etnry和WAL日志,因为WAL日志本身都没有持久化成功,所以Consensus Log就根本不可能持久化成功。
在根据这三个节点的现有状态,就是根据X-Paxos协议,当前Leader会发现LogIndex为2的日志在多数派节点上已经都持久化成功了,所以在Consensus Log层面的Commit Index是2,转化成LSN之后,Commit LSN也就是300,这是我们针对LSN多数派同步的基本原理。
(二)进程/线程架构
下面讲一下实现层面的几个关键点
• 独立的consensus进程提供多数派日志同步服务
• WAL日志同步采用原生流复制逻辑
首先是进程和线程的架构,PG本身是多进程的架构,而X-Paxos是多线程架构,这里面会涉及到的一些问题,就是PG的进程本身无法直接调用X-Paxos的接口,或者它无法直接访问X-Paxos内部的私有内存,所以我们最终选择以X-Paxos为基础,然后新引入了一个Consensus的服务进程。
进程内部会包含多个线程,然后其中包含了X-Paxos的多线程,对外它主要提供多数派日志提交以及相关服务。这个服务是一个常驻的进程,就是说当startup进程启动之后,在这个进程中启动。
首先介绍一下Consensus进程内部的几类线程,第一部分就是X-Paxos内部的一些线程,包括I/O线程和工作线程。I/O线程主要负责节点间的网络通信,工作线程主要负责协议的处理,比如发起选举,比如Follower节点要处理日志的Append请求,可能需要发起选举的请求,主要是跟一致性协议、算法相关的处理逻辑。
除了这些线程外,Consensus进程本身又引入了四类线程,首先是 Append Thread。刚才在原理里提到说,我们会对每一段的WAL日志生成Consensus log,这主要是由 Append Thread 来完成的。它根据当前 WAL 日志的回刷位点来生成Consensus log etnry,然后把log etnry传递给X-Paxos进行同步。
第二类线程是 Advance thread,是推进WAL日志的提交位点,它主要就是从X-Paxos中获取当前 Consensus log 的提交位点,然后把它转化成LSN位点。另外,Advance thread同时会根据Consensus协议层的一些状态变化,来驱动PG层进行状态变更。比如Consensus层发生了切除操作,那么Advance层检测到切除的动作或事件之后,它就会触发驱动Postmaster或startup进程进行一些相应的变动,这个下文会具体阐述。
第三个是Command 线程,它主要是处理一些集群变更的命令。比如Leader切换、增删节点等命令,一般来说在PG内部的处理方式是用户发生的请求一般由Server Process处理,但Server Process本身又无法直接调用X-Paxos内部的一些接口,所以我们的处理方式是Server Process把所有的管理请求放到一个共享队列里,然后由Command threads从共享队列里面拿请求来处理,处理完成之后再把执行结果放到执行队列里面去,然后返回给Server进程。
第四类是统计类的线程,这一类线程主要是处理一些状态和统计信息获取的类型图,也就是获取X-Paxos协议内部的一些状态。整个处理的逻辑跟集群变更的命令差不多,也是通过共享队列进行交互。
接下来讲一下Consensus内部的内存情况,以及它的并发控制机制。
内部内存主要分为两种,一种是X-Paxos本身分配的私有内存,这部分内存只有Consensus内部的一些线程,包括X-Paxos线程,以及刚才提到的Server Threads。Server Threads包括 Command Threads和统计的Statistics线程。
除了这一类内存之外,另外一种内存可以由X-Paxos线程,Consensus线程以及PG进程都可以访问的共享内存。这些内存主要是进行Consensus process以及 PG进程之间的一些状态交互,这类内存的访问接口必须是线程安全的。对于PG侧的一些共享内存,Consensus Process内部可能也是需要访问的,这时候我们必须要确保Consensus Process内部只有一个线程会访问这些内存,可以认为Consensus Process来访问这块内存。
第三个是Consensus Log的管理,这里能确保只有Consensus process内部的一些线程进行读写。
以上是整个进程和线程的架构。
《PolarDB for PostgreSQL源码与应用实战》——PolarDB for PostgreSQL高可用原理(下) https://developer.aliyun.com/article/1232677?groupCode=polardbforpg