天天看点

PostgreSQL standby recover的源码分析 (walreceiver唤醒时机? 为什么standby crash后walreceiver不会立即被唤醒?)

postgresql , 流复制 , stream replication , wal receiver , 唤醒时机 , 状态机

前段时间有位网友提的问题,

当postgresql数据库的standby节点crash后再启动,发现standby节点的wal receiver进程很久才启动并开始从主节点接收wal。

这段时间是在等待standby节点恢复pg_xlog目录中已有的xlog日志。

PostgreSQL standby recover的源码分析 (walreceiver唤醒时机? 为什么standby crash后walreceiver不会立即被唤醒?)

这是为什么呢?

postgresql在crash后,需要从最近的一个检查点开始恢复,因为在每次检查点(如果是standby可能称为restart ckpt)开始后,发生变更的块都会记录对应的full page到wal日志文件中,从检查点恢复,可以保证不会因为操作系统的partial write 导致数据块的不一致。

PostgreSQL standby recover的源码分析 (walreceiver唤醒时机? 为什么standby crash后walreceiver不会立即被唤醒?)

对于standby节点也是一样的,启动时,从最近的一次检查点开始恢复,它可以选择ckpt或者restart ckpt开始恢复,选最近的即可。

在standby上也可以创建检查点(称为restart ckpt),后面会讲到。

这里只解释了一个问题,就是数据库crash后从哪里开始恢复,还有一个问题没搞明白?

为什么wal receiver进程很久才启动并开始从主节点接收wal?

得先讲讲数据库有几种恢复来源:

3种恢复来源,

然后我们再分析一下,它如何选择恢复来源的?

1. 首先会找pg_xlog目录有没有需要的日志(包括tli),如果有,会从日志目录中直接读取pg_xlog进行恢复,直到pg_xlog目录中没有需要的文件,则会将source置为下一个可用的source。

2. 当source=stream时,并且wal receiver进程没有启动时,发信号启动它。

3. 如果stream失败,会重新扫描tli,然后等待wal_retrieve_retry_interval这个时间间隔,

循环往复。

PostgreSQL standby recover的源码分析 (walreceiver唤醒时机? 为什么standby crash后walreceiver不会立即被唤醒?)

代码详见

整理一下启动wal receiver的流程如下

PostgreSQL standby recover的源码分析 (walreceiver唤醒时机? 为什么standby crash后walreceiver不会立即被唤醒?)

相关代码

src/include/storage/pmsignal.h

src/backend/postmaster/postmaster.c

src/backend/replication/walreceiverfuncs.c

src/backend/access/transam/xlog.c

standby创建的检查点称为restart ckpt, 目的是防止正常的停止standby后,还要从正常的检查点位置开始恢复。

建立了restart ckpt后,standby重启时,从restart ckpt开始恢复即可。

PostgreSQL standby recover的源码分析 (walreceiver唤醒时机? 为什么standby crash后walreceiver不会立即被唤醒?)

src/backend/postmaster/checkpointer.c

了解了原理,我们来想想现在的机制会存在什么问题。

1. 如果备库恢复速度较主慢,接收到的1024mb日志,只恢复到了512mb,然后wal receiver进程突然挂了。

此时,standby会将恢复source切到pg_xlog或resotre_command,由于pg_xlog里面还有512mb没有恢复,那么会等这512mb恢复完后,才会发生source的切换,再次唤醒wal receiver。

2. standby crash,没有产生shutdown restart ckpt.

crash后重启,需要从最近的restart ckpt或者ckpt进行恢复,如果这之间有许多pg_xlog,那么也需要恢复一段时间,从而wal receiver的唤醒时间也会被拖长。

带来的问题就是:主库和备库的sender wal位点差异会受到一定的影响。如果正好此时主库挂了,缺失的日志可能会比较多。

1. 备库在接收xlog时,记录xlog接收到的位点信息,从而xlog不需要等apply位点来获取状态。

2. 并行接收,不要等apply请求唤醒wal receiver,使用独立的进程receive。

但是可能引入另一个问题,比如备库就是apply较慢,导致没有apply的xlog堆积在备库的pg_xlog目录。

3. 并行恢复,由于postgresql是物理的备库,效率已经很高了,通常不需要并行恢复,首先要考虑的是备库的iops能力。