开发者学堂课程【PolarDB for PostgreSQL 入门:PolarDB for PG 高可用原理】学习笔记,与课程紧密联系,让用户快速学习知识。
课程地址:https://developer.aliyun.com/learning/course/813/detail/13919
PolarDB for PG 高可用原理
内容介绍
一、高可用集群架构
二、一致性协议复制实现
三、代码结构
一、高可用集群架构
他是把物理复制和一致性协议相结合来保证各个节点上的末日志的强一致性,所以先了解一下源生 PG 的物理方案也就是流复制方案。
PG 源生的流复制方案支持一步、同步和 quorum。
同步复制:
主要目标是保证不丢数据,但会有很多问题,无法满足可用性或者主库的 rt,在就是不具备故障自动切换的能力。旧的主库在发生故障之后无法再加入到一个集群里面。比如说事物在主库上 ui 经保存成功,但是在备库上还没有收到日志,主动出现故障,通过手动管控系统来备份成新的主库,一段时间后旧的主库恢复之后,正常来说会从新的主库来同步日志,但是在旧的主库不可使用之前会有多余的日志,所以无法从主库上直接拉取日志,会发现拉取的日志已经存在,在流复制里面会失败。严格意义上来说,同步复制不能严格保证不丢失数据。
异步复制:
只关心发送,但不关心是否同步完全,跟同步复制比较,不需要等待日志持久化之后主库事物才能提交,可用性和性能比较好,问题就是存在丢失数据。
Quorum 复制:
看起来是在使用了一个多数派方案时不丢失数据,但其实本身在一致性里面除了多数派同步方案之外,它本身还有选组、日志一致性,集群在线变更这些方面的逻辑,但是 Quorum 复制并没有涉及这些逻辑,所以本质上来讲,他并不是一个完整的 rpu 的高使用方案。
PolarDB for PG 可用方案
基于 X-Paxos 的一致性协议复制,在阿里稳定使用了很长一段时间,基本上和其他的一致性协议是类似的。
首先把一致性协议引用到物理复制里面,是整个的一个高可用集群是一个单点写入,多点可读的一个集群系统,其中leader 节点它是可以作为单点的写入节点来对外提供读写服务,产生的日志之后,它会像其他节点进行同步,follower 节点它是不可写入的,他主要是接受来自于 leader 的一个日志,进行回放,最终他其实是可以对外提供一个只读服务的。
高可用系统特点:
1.集群里数据的强一致性,RPO = 0,当多数派日志写入成功后,才认为日志提交成功。
2.少数派节点故障时,不影响可用性。可以自动增删节点,不影响运营,这一点是由一致性来保证的,最终就会达成一致性。
3.自动 failover,当发生 failover 之后,日志首先回和 leader 对齐,再次从 leader 上拉取日志。主要是通过一致性协议来进行对齐。在高可用集群里面,只要半数以上的节点存活就能保证节点正常对外服务,但是当 leader 节点发生故障的时候,就会自动触发选组流程,由新组对外进行读写服务,follow 节点也会自动从 leader 节点上同步日志。
4.支持跨 AZ/跨域部署,包含同机房三副本,同城三机房三副本,以及两地三机房的无副本技术。跨域是可以用leader 节点进行灾备,但不影响 leader 的可用性。
5.兼客原生流复制/逻辑复制,保证下游的消费者不受影响。
另外两个节点
Logger:有选举权,不保留数据,仅保留实时日志。
Learner:无选举权,只读数据节点。一般是加节点的中间状态或者灾备节点。一般有 leader 节点时不使用。先使用他来同步数据,数据基本上追上的时候再把它升级成一个 follow 节点。主要特点是,发生切换之后,从新的节点上同步日志。
二、一致性协议复制实现
1. 基本原理:
原生异步流复制进行 WAL 日志同步
Consensus 协议推进 WAL 日志的多数派同步/提交位点+Consensus 日志中仅记录 WAL 日志位点
Consensus 日志持久化前,确保对应的 WAL 日志已持久化 Consensus 日志提交成功,表示对应的 WAL 位点提交成功
Leader 节点上已经有三段日志 commit LSN,第一段是100-200,第二段是200-300,第三段是300-450,生成的分别是1、2、3。这里引用了一个持久化的依赖,每个 logindex 在持久化的时候必须确保专业位点的日志已经持久化成功,对应位点的末日志已经持久化成功。Follow1虽然日志已经持久化成功,但是由于并没有接收到,所以consensus=3这个并没有持久化成功。
Follow2上都没有持久化成功,日志根本就没有持久化成功,所以 consensuslog 就不能持久化成功。根据这三个节点的现有状态,根据 X-Paxos 协议,当前 leader=2的日志在多数派节点上已经都持久化成功了。
2. 进程/线程架构
PG 本身是一个多线程架构,而 X-Paxos 是一个多进程架构部。问题就是 PG 进程无法直接访问 X-Paxos 内部的稀有内存。所以选择以 X-Paxos 为基础,选择了一个 consensus 服务进程,这个进程内部包含多个线程,其中包含了X-Paxos 多线程。对外提供多数派日志提交以及相关服务,这个服务是一个常驻的进程。
介绍一下 consensus 进程内部的几类线程,第一步就是 X-Paxos 内部线程,包括 io 线程和工作线程,io 线 程主要复制节点之间的网络通信,工作线程主要来进行协议的处理,比如发起选取,跟一致性算法相关的一些逻辑。consensus 本身就引入了4类线程。
Command threads:执行集群管理命令,比如 leader 切换增删节点等。
一般来说,在 PG 的内部处理方式是用 Scrver priaess,所以解决方式是把 Scrver priaess 把所有请求方式放在一个共享队列里面,由 Command threads 拿到请求来处理,处理之后把结果放到队列里面去,然后返回给 Scrver进程。
Statistics thread:执行状态/统计信息获取市令。主要处理状态或统计类信息的获取类进程,主要获取协议内部状态,也是通过共享队列来进行交互的。
Append thread:根据 WAL 回刷位点生成并 append consensus etnry.然后传给 X-Paxos 同步,
Advance thread:推进 Consensus 状态和末日志提交位点。同样也会根据 Consensus 协议状态的变化来驱动PG状态变更,比如 Consensus 发生切主操作,那 Advance 检测到切主动作后,会驱动一些进程进行相应的变动。
consensus 内部的内存情况包括 X-Paxos 线程和 serve thread。
除了这类内存,还有X-Paxos线程、Consensus 线程和 PG 进程都可以访问的共享进程,这些内存主要是进行一些Consensus 和 PG 之间的交互,这类内存的访问接口必须是线程安全的,另外是对于 PG 内存一些共享内存,Consensus 可能也是需要访问的,这个时候必须确保说 Consensus 只有一个线程回访问这些内存,这个时候可以认为是 Consensus pracess 来访问这块内存。
后面是 Consensuslog 的一些管理,这里就必须确保 Consensus 内部的线程读写。这是整个线程和进程的架构。
3. 一致性协议复制流程
当 PG 进程需要提交末日志的时候,首先是本地需要写入末日志,开始等待超过要提交的位点,等待提交请求之后,会在两个电路上闭行进行通铺,在流复制整个电路上和源生的逻辑是差不多的,首先通过 writer cntry 在来发送日志,在 follower 节点上由 wal receiver 来接收并持久化日志。
在 Consensus 提交电路上主要流程是 Consensus log cntry 首先写入本地之后传递给 s responce append 发送到follow 节点,
在 follower 节点上接收到 Consensuslog 之后,首先要确认位点的日志持久化成功了,之后就可以在本地写入Consensus log,完成之后 leader 本地持久化成功。Leader 节点本身会根据所有节点来确定最新的持久位点,之后会把提交位点转化。复杂的是在当节点发生变化时怎样来进行协同的处理。
4. Consensus 日志存储
X-Paxos 本身只是负责强日志的同步,持久化还是通过api外部来实现,在 X-Paxos 内部有两类日志,一个是与日志同步本身相关的正常的 log cntry,这类日志除了与一致性相关的信息,本身还会自带一个 crc,除了这些之外,他只是包含了对应末日志结束,所以他的成分是固定的,这类日志是在 Consensus 里面占大多数的,第二类是 X-Paxos协议本身会产生一些成员变更、集群配置变更的日志,因为它里面要包含一些节点的信息,这类日志肯定是便程的,但是这类日志占比例很小,所以在日志存储这里结合大部分日志都是固定成分特点,采用了定长日志和变长日志混合存储方案。
目标:日志读写高性能
页面定位:定长日志和变长日志索引统一存储,少量变长
日志的 Payload 部分独立存储。
pageid=log_index/entry_per_page entry_id=log_index%entry_per_page
页面缓存和查找:LRU 替换策略
日志的持久化还是需要 VPI 外部方式来实现。在一致协议中,有两类日志
定长日志(kNormal-gkNoop):0/0
变长日志(kConfigChnge):Payload 交件中的偏格/长
当日志读取的时候首先根据日志类型来选取是定长日志还是变长日志,如果是定长日志就可以之间获 rasen 信息,基本上就可以获取到一个完整的信息了,如果是变长日志,根据日志中记录的偏移和长度来去变长日志中来读取他的相关信息。
这样的好处是当要大部分获取定长日志的时候,可以直接定位日志所在的页面以及页面上的位置和偏移。这样主要是可以加速页面的定位,另外是引入了一个页面缓存,同时也引入了一个替换的相关策略。
5.Consensus 状态机与 DB 状态机协同
(1)主动探测 Consensus 状态变化,触发 Postmaster 状态变更信号
(2)Pastmaster 进程处理变更信号:
Slave:通知 Startup 进程 Recovery 状态变更,包括截断 WAL 日志、重建流复制或者 promote 等。
Master:降级则重启进入 Recovery 状态
主要根据 Consensus 状态变化来产生一些事件。
DB 测根据收到信号之后来推测信号的一些变化
这里会涉及日志对齐的问题,比如发生一个切主的状态变化之后,如果 follwer 节点上从旧主上拉取的末日志,在新主上不存在,为解决这个问题,给末日志也赋予一个 tern 状态,只有当末日志状态推入到最新之后,相应的Consensus log 才能持久化。
在一致性协议里面 Consensus log 本身也会出现截断的情况,这种情况下本地的末日志也是被截断的。
在以上两种状态之后末日志被截断之后 DB 测的数据状态这些的处理方法在回刷磁盘之前,必须确保末日志在多数派上提交成功,所以持久化之后的状态不会回退。因为在一致性协议里,不会本身提交的状态日志被截断,另外是在follwer 节点上,只有日志在提交之后才会回访,这个时候不管是缓存还是持久化状态,都不可能出现回退的情况,想到缓存,leader 本身可能是先修改数据的 bugfur,之后会持久化日志,所以出现 leader 降级之后,需要回退catch情况,这里就直接重启了。
双状态机协同-示例1
首先 Consensus 进程探测到 term 和 leader ID 发生了变化,要额外获取 leader 的信息,把 ID 转化为 IP 和 port,获取一下 Consensus log 的日志位点,之后直接修改 process master 状态之后触发信号,
process master 收到的信号就是leader发生的变化。这个时候会修改 Status up 进程的状态。接下来通知 status up进行一个状态变更,
它本身就会不断检测自己的本身状态,当他发现 leader 状态发生变化,就会选择重启流复制,从新的 leader上拉取日志。
三、代码结构
主要代码修改
1.)Consensus进程管理和提交主流程
Consensus Replication
sre/backend/polar dma/consensus repl.c
Consensus进程管理机提交主流程
src/backend/polardma/consensus log.c
Consensus日志管理
src/backend/polar dma/consensus slrw.c
Consensus 日志页面缓仔和持久化
2. )Consensus 管理文件
Streaming Replication
src/backend/access/transam/xlog.
src/backend/replication/walsenderc
src/backend/replication/walreceiverc
幕于 consensus 状态管理流复制等
3.)具体管理和持久化
C/C++wrapper
src/backend/polardma/libconsensus/polarwrapper/polar consensus.cc
对于 X-Paxos 相关接口的cwrapper
src/backend/polar dma/libconsensus/polar wrapper/polar consensusog.cc
对于志管评按口的 c++wrapper
4.)流复制
X Paxos
src/backend/polar_dma/libconsensus/consensus
X-Paxos 尊法实观
src/backend/polar_dma/libconsensus/dependency/easy
X-Paxos 依移的高性能网络编程框架
Logger syncer
sre/backend/polar datamax/polar datamax.c
日志同步服务(日志节点)