PostgreSQL数据库同步流复制源码分析

之前网上搜过一些帖子,大佬们一般都是点到即止。最近尝试把分析过的代码记录下来,希望能尽量写的详细吧。

同步流复制通过配置synchronous_commit参数提供了不同的事务可靠性级别:

off 表示commit时不需要等待本地wal持久化

local 表示commit时需要等待本地数据库的wal持久化

remote_write 表示commit需要等待本地wal日志持久化,同时需要sync standby节点将收到的日志写入wal buffer

on 表示commit需要等待本地wal日志持久化,同时需要sync standby节点的wal持久化

remote_apply 表示需要等待本地wal日志持久化,同时需要备机节点的wal日志重放

由此可知同步流复制模式下的可靠性级别至少也是remote_write。

那接下来就细致讨论同步流复制过程:

示例如图所示,是一主三备的同步流复制集群,有三个客户端连接,对于每一个备库receiver进程都会有唯一的sender进程与之对应。

1.当客户端接收DML操作后,通过编译器,执行器生成执行计划后,将会开启一个事务执行,在内存中对数据进行修改产生脏数据的同时,同时在内存的wal buffer中产生redo日志,wal write进程负责在事务提交后将产生redo日志不断的flush到日志文件。

2.sender进程负责将落入磁盘的日志数据读入buffer包装后发送给备库,receiver进程不断将日志写入wal buffer,再由备库的wal write将日志flush到磁盘,等待startup进程将落盘的日志在备库apply。主库事务commit后需要等待备库的回复才能够返回客户端,继续工作。

(关于这部分内容会在后面的文章中再一一详解)

那如果一直得不到备库回复会如何呢??

主库将会被悬挂,直到得到备库的响应。这篇文章主要讲主库是如何在事务提交之后成功返回客户端,也算是同步流复制的核心。

1.以上图为例,三个backend进程并发执行,产生了三份日志,即有了三个LSN,每一个sender将三份日志分别发送给三个备库。当有事务提交后则将与之对应的backend进程加入到等待队列中。

2.备库将已经write或者flush的位点回复给主库,更新对应的sender的write或flush,之后在进行等待的后端进程释放时会遍历sender数组中的每个sender进程的write和flush点,对点进行排序,取最小的LSN,之后和全局的LSN做比较,全局LSN记录备库已经write或者flush的日志。大于全局的LSN则将backend进程从等待队列中取出。因为三个walsender都处于工作台,所以每个sender都可以进行backend进程的释放,而不需要等待三个sender同时完成对一个后端进程的释放。(哈哈,还是觉得很精妙啊)

)那一个sender可以同时释放几个后端进程呢?

每个后端进程都记录了一个执行事务的WaitLSN,所以在sender进行释放的过程中,也会遍历等待队列,比较waitLSN和全局LSN的大小,小于小于等于全局LSN,则将后端从等待队列取出,返回客户端。

记录几个重要的结构体,和函数:

SyncRepWaitForLSN:事务提交后将后端进程加入等待队列,获取waitlatch锁。

SyncRepGetNthLatestSyncRecPtr  :将备库回复的LSN排序

SyncRepWakeQueue:释放等待队列

struct PGPROC:后端进程结构体

struct WalSndCtlData:维护同步数据信息的全局结构体

struct WalSnd:每一个sneder进程的结构体

note:LSN是指日志的序列号。日志序列号是由文件名+日志偏移量组成(有兴趣的可以看一下有关MySQL的LSN)。

猜你喜欢

转载自my.oschina.net/u/3842863/blog/1800692