postgresql , 流复制 , stream replication , wal receiver , 唤醒时机 , 状态机
前段时间有位网友提的问题,
当postgresql数据库的standby节点crash后再启动,发现standby节点的wal receiver进程很久才启动并开始从主节点接收wal。
这段时间是在等待standby节点恢复pg_xlog目录中已有的xlog日志。
这是为什么呢?
postgresql在crash后,需要从最近的一个检查点开始恢复,因为在每次检查点(如果是standby可能称为restart ckpt)开始后,发生变更的块都会记录对应的full page到wal日志文件中,从检查点恢复,可以保证不会因为操作系统的partial write 导致数据块的不一致。
对于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这个时间间隔,
循环往复。
代码详见
整理一下启动wal receiver的流程如下
相关代码
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开始恢复即可。
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能力。