说明: 下文的部分内容节选自《PostgreSQL实战》
PG的延迟复制
参数: recovery_min_apply_delay
某些情况下,一个后备服务器会尽快恢复来自于主服务器的 WAL 记录。有一份数据的延时拷贝是有用的,它能提供机会纠正数据丢失错误。这个参数允许你将恢复延迟一段固定的时间,如果没有指定单位则以毫秒为单位。例如,如果你设置这个参数为5min,对于一个事务提交,只有当后备机上的系统时钟超过主服务器报告的提交时间至少 5分钟时,后备机才会重放该事务。
有可能服务器之间的复制延迟会超过这个参数的值,在这种情况下则不会增加延迟。注意延迟是根据主服务器上写 WAL 的时间戳以及后备机上的当前时间来计算。由于网络延迟或者级联复制配置导致的传输延迟可能会显著地减少实际等待时间。如果主服务器和后备机上的系统时钟不同步,这会导致恢复比预期的更早应用记录。但这不是一个主要问题,因为这个参数有用的设置比服务器之间的典型事件偏差要大得多。
只有在事务提交的 WAL 记录上才会发生延迟。其他记录还是会被尽可能快地重放,这不会成为问题,因为 MVCC 可见性规则确保了在对应的提交记录被应用之前它们的效果不会被看到。
一旦恢复中的数据库已经达到一致状态,延迟就会产生,直到后备机被提升或者触发。在那之后,后备机将会结束恢复并且不再等待。
这个参数的目的是和流复制部署一起使用,但是,如果指定了该参数,所有的情况下都会遵守它。使用这个特性也会让hot_standby_feedback被延迟,这可能导致主服务器的膨胀,两者一起使用时要小心。
延迟备库的搭建很简单, 只要在 recovery.conf 里面增加个配置项即可
recovery_min_apply_delay = 1min # 这里我测试就设置1分钟的延迟
## 默认的支持时间单位为 ms 、s、min、h、d 即 毫秒 秒 分钟 小时 天
注意:修改后,需要重启 standby节点才能生效。
然后,在主库创建表并插入一条测试数据:
postgres=# create table test_delay(id int4,create_time timestamp(0) without time zone);
postgres=# insert into test_delay (id,create_time) values (1,now());
然后,等一分钟左右到延迟standby节点去查看下数据是否同步过去
延迟复制场景下 recovery_min_apply_delay 参数对同步复制的影响
同步复制情况下, 通常要 synchronous_commit 配置为 on 或 remote_apply
on 表示 standby将wal接收到 --> 写入wal日志文件 --> 向客户端返回成功。
standby表示 standby将wal接收到 --> 写入wal日志文件 --> 并应用到standby --> 才会向客户端返回成功。
下面对 synchronous_commit 不同参数下,并且设置了延迟复制的测试:
场景1: synchronous_commit=on 并且 recovery_min_apply_delay = 1min
注意:
synchronous_commit是设置在主库的postgresql.conf中的(支持会话级别设置,也可以修改配置文件reload后全局生效)。
recovery_min_apply_delay 是设置在standby的recovery.conf中的。
这种场景下, 我们在主库上插入一条数据,主库会立即返回执行成功or失败的结果。 然后我们到延迟复制的standby去查询,发现还是会需要1min后才能查到这条数据。
也就是说, 延迟备库场景下, synchronous_commit 配置为on时 和 异步流复制是一致的。
场景2: synchronous_commit=remote_apply 并且 recovery_min_apply_delay = 1min
注意:
synchronous_commit是设置在主库的postgresql.conf中的(支持会话级别设置,也可以修改配置文件reload后全局生效)。
recovery_min_apply_delay 是设置在standby的recovery.conf中的。
这种场景下, 我们在主库上插入一条数据,主库会hang住等待1min(等待从库完成apply操作)后,然后才能返回执行成功or失败的结果。
然后我们到延迟复制的standby去查询,发现立即就能查到这条数据。
也就是说, 延迟备库场景下, synchronous_commit 配置为 remote_apply时,会造成主库上面的事务的提交的阻塞。
生产环境用到延迟从库的场景下,一定要避免设置 synchronous_commit=remote_apply (当然从性能角度考虑也很少会设置为remote_apply的)