本文主要介绍亚信安慧AntDB数据库的分布式架构下的特有进程之snapshot receiver的设计,这也是分布式架构的核心进程之一。
进程简介
该进程的作用从逻辑上解释包含两个方面:
- 同步快照,并且是作为通信的client端存在
- 同步事务号,同样也是client端(注)
与此同时,该进程只存在于Coordinator节点(后文简称CN节点)和Datanode节点(后文简称DN节点)上,且主机和备机上都有,这些节点上有且仅有一个该进程。
(注:通信的server端则是snapshot sender进程,这部分内容笔者将会在另一篇文章中阐述)
进程的内存结构
Snapshot receiver进程在物理上并没有特别的文件存储方式,启动进程时同时会申请共享内存,所有的数据都在这部分内存之中。
内存结构名为SnapRcvData,代码内通常以SnapRcv指针来访问这部分内存,完整的结构体定义较为琐碎,为了便于读者理解,这里仅挑选部分重要的内容给出说明,完整的定义参考源码文件(src/backend/replication/snapreceiver.c):
- mutex:访问这部分共享内存时要加的锁
- gxid_mutex:访问事务号成员时要加的锁
- geters:backend进程等待申请事务号时的队列,等待receiver进程响应申请事务号的请求
- reters:已获取事务号的backend队列,等待backend取走事务号
- xcnt:当前集群内活跃事务号的个数
- xip[MAX_BACKENDS]:当前集群内活跃事务号的列表
- wait_finish_cnt:本节点申请的事务号个数,仅CN节点会有这个值
- wait_xid_finish[MAX_BACKENDS]:本节点申请的事务号列表,仅CN节点会有这个值
- latestCompletedXid:当前集群内最大的已完成的事务号
- global_xmin:当前集群内的xmin(分布式集群内最老的活跃事务号)
- local_global_xmin:本节点内的xmin(当前节点内最老的活跃事务号)
- global_finish_id:本节点最近一次完成的事务号
进程的通信协议
“通信”特指与snapshot sender进程之间的通信,这里借用了逻辑复制的通信格式,采用了‘libpqwalreceiver’库的接口,使用到的接口的代码如下(src/backend/replication/libpqwalreceiver/libpqwalreceiver.c):
- walrcv_connect
- walrcv_disconnect
- walrcv_startstreaming
- walrcv_send
- walrcv_receive
- walrcv_get_senderinfo
不难看出,各接口的用途与名称有一定的必然联系,首先是建连,然后开始流复制,并通过send和receive传输数据。当snapshot receiver进程启动时,会主动连接snapshot sender进程,处理过程如下图所示:
图1:snapshot receiver进程启动处理过程
建连成功之后会调用walrcv_startstreaming,把一些流复制选项同步到服务端,当服务端确认之后就会切换为copy-both模式,这时即表示可以正式传输数据了。当然,snapshot receiver进程同步的内容并不是流复制相关内容,而是事务号相关的内容,包括以下两个方面:
- 下一个待分配的事务号(nextFullXid)
- 本节点残留的2pc事务号列表,如果没有即为空(注)
(注:这部分属于进程的异常处理逻辑,笔者会在后续的文章中专题剖析)
建连完毕之后,进程即进入正式的通信阶段,一般来说,进入这个阶段集群基本已经启动完毕了,receiver和sender之间通过不同的消息类型去完成不同场景的需求,下面逐个介绍receiver进程的通信逻辑:
- 消息‘a’
负责处理sender进程的广播消息,消息内容是其他CN申请的事务号,其接口是SnapRcvProcessAssign,处理步骤大致如下:
1、锁住共享内存(SnapRcv)
2、从消息流获取xid,然后遍历SnapRcv->xip数组,做去重处理,然后放入该数组内,同时SnapRcv->xcnt加1
3、解锁
4、如果新的xid大于等于本地的ShmemVariableCache->nextFullXid,并且不是备节点,就需要扩展新的clog,同时更新nextFullXid
- 消息‘g’
负责处理申请事务号后sender进程反馈回来的数据,其接口是SnapRcvProcessAssignRequest,处理步骤大致如下:
1、锁住共享内存(SnapRcv)
2、从消息流获取xid和procno,然后遍历SnapRcv->reters队列,找出正在等待事务号的进程对应的procno
3、找到进程之后,xid赋值给proc->getGlobalTransaction
4、然后setlatch,通知backend进程,backend进程会取出proc->getGlobalTransaction返回给上层调用
5、解锁
- 消息‘c’
负责处理sender进程的广播消息,消息内容是其他CN结束的事务号,其接口是SnapRcvProcessComplete,处理步骤大致如下:
1、锁住共享内存(SnapRcv)
2、从消息流获取xid,然后遍历SnapRcv->xip数组,若找到就把这个xid从该数组内删除,同时SnapRcv->xcnt减1;如若没找到,会报ERROR
3、同时,如果该xid大于SnapRcv->latestCompletedXid,那就更新SnapRcv->latestCompletedXid的值
4、唤醒正在等待该事务提交的backend进程
5、解锁
6、用最新的SnapRcv->latestCompletedXid值来更新本地的ShmemVariableCache->latestCompletedXid
- 消息‘f’
负责处理结束事务号后sender进程反馈回来的数据,其接口是SnapRcvProcessFinishRequest,处理步骤大致如下:
1、锁住共享内存(SnapRcv)
2、从消息流获取xid和procno,然后遍历SnapRcv->wait_commiters队列,找出正在等待的进程对应的procno和xid
3、找到进程之后,proc->getGlobalTransaction的值恢复为InvalidTransactionId
4、然后setlatch,通知backend进程
5、解锁
- 消息‘s’
负责处理sender进程同步来的快照数据,其接口是SnapRcvProcessSnapshot,处理步骤大致如下:
1、从消息流获取快照、xmin和latestCompletedXid三份数据
2、锁住共享内存(SnapRcv)
3、用gtm传来的值更新SnapRcv->latestCompletedXid
4、把快照数据整体memcpy到SnapRcv->xip数组
5、唤醒等待中的进程
6、解锁
7、更新SnapRcv->global_xmin(这个值在backend获取快照时,用于更新snapshot->global_xmin)
- 消息‘h’
处理心跳消息的反馈,其接口是SnapRcvProcessHeartBeat,处理步骤大致如下:
1、接收的心跳包内包含2个内容,时间戳和xmin
2、接收到心跳之后,更新SnapRcv->gtm_delta_time和SnapRcv->global_xmin
相对应的,还有一个发送心跳包的接口为SnapRcvSendHeartbeat,这个并不是‘h’消息的处理内容,而是SnapReceiverMain主函数的流程,处理步骤大致如下:
1、每次循环到对应的处理阶段(参考图2的流程),判断当前时间是否超过了设定的心跳间隔时间阈值(snap_receiver_timeout),这个是guc参数,默认60s
2、 如果超过了阈值,就发送心跳包,否则跳过
3、发送的内容同接收一样,时间戳和xmin
- 消息‘u’
sender进程会通过这个消息,把nextFullXid同步到所有其他节点,其接口是SnapRcvProcessUpdateXid,处理步骤如下:
1、sender发来的只有一个事务号,即nextFullXid(下一个待分配的事务号)
2、如果拿到的xid比本地ShmemVariableCache->nextFullXid的要大,就更新ShmemVariableCache->nextFullXid和ShmemVariableCache->latestCompletedXid
3、最后更新SnapRcv->latestCompletedXid(先锁后更新)
进程的工作主流程
Snapshot receiver进程整体上同其他辅助进程相类似,启动之后会处于一个无限循环之中,不断监听各种事件和信号,笔者整理了一份流程图帮助读者理解,如下图所示:
图 2:Snapshot receiver进程工作主流程图
上述流程图对应的既是SnapReceiverMain函数,又是snapshot receiver进程的主函数,之前的各种消息处理都是从这里开始的,想深入理解各种细节的读者可以从这里入手。
小结
本文介绍了snapshot receiver进程在正常情况下的处理逻辑,通过流复制协议,配合以不同的消息处理函数,完成了分布式事务号、分布式快照的同步功能。一个进程不可能总是处于正常状态,那当异常发生时,AntDB如何来保证事务的一致性呢?敬待后续的专题分享。
关于亚信安慧AntDB数据库
AntDB数据库始于2008年,在运营商的核心系统上,服务国内24个省市自治区的数亿用户,具备高性能、弹性扩展、高可靠等产品特性,峰值每秒可处理百万笔通信核心交易,保障系统持续稳定运行超十年,并在通信、金融、交通、能源、物联网等行业成功商用落地。