延迟备库是指可以配置备库和主库的延迟时间,这样备库始终和主库保持指定时间的延迟,例如设置备库和主库之间的延迟时间为1小时 ,理论上备库和主库的延时始终保持在一小时左右。
延迟备库
1 延迟备库的意义
PostgreSQL 流复制环境下,如果主库不是很忙并且备库硬件资源充分,通常备库和主库的延时能在毫秒级别。如果主库上由于误操作删除了表数据或删除表时,从库上的这些数据也瞬间被删除了,这时,即使对数据库做了备份,要恢复到删除前的状态也是有难度的,比如,如果使用 pg_dump 做了逻辑备份,通常是按天、按周、按月进行逻辑备份等,也只能恢复到最近逻辑备份时刻的数据,除非是做了基准备份并且开了归档,这时可以利用全量备份和归档恢复到删除前的状态,从而找回被删除的数据,当然这种方法维护成本较高。在这一场景下,延迟的备库在一定程度上缓解了这一问题,因为在设置的延迟时范围内,备库上的数据还没被删除,可以在备库上找回这些数据,这节将详细介绍延迟备库的配置和使用,当然,如果超过了已设置的主备延迟时间才发现主库上的数据被删除了,这些数据在备库也找不回来了。
2 延迟备库部署
测试环境依然为一主一备异步流复制, host1 主库, host2 为备库,延迟备库的配置非常简单,只需要配置 recovery_min_apply_delay 参数, 参数位于 recovery.conf 配置文件,语法如下:
recovery_min_apply_delay (integer)
此参数单位默认为毫秒,目前支持的时间单位如下:
- ms (毫秒,默认单位)
- s (秒)
- min (分钟)
- h (小时)
- d (天)
流复制主库提交事务后,主库会将此事务的 WAL 日志流发送给备库,备库接 WAL 日志流后进行重做,这个操作通常瞬间完成,延迟的备库实际上是设置备库延迟重做 WAL 的时间,而备库依然及时接收主库发送的 WAL 日志流 ,只是不是一接收到 WAL 后就立即重做,而是等待设置的时间再重做,假如设设置此参数为一分钟,流复制备库接收到主库发送 WAL 日志流后需等待一分钟才重做。
我们将 host2 上备库的此参数设置1分钟,如下所示:
recovery_min_apply_delay = 1min
以上代码将主库和备库的延迟时间设置为1分钟,之后重启备库使配置生效, 如下所示:
pg_ctl restart
之后在主库上创建 test_delay 测试表,如下所示:
postgres=# CREATE TABLE test_delay(id int, create_time timestamp(0) without time zone);
CREATE TABLE
这时备库上等了大概一分钟才看到这张表,接着在主库上插入1条数据,如下所示:
postgres=# INSERT INTO test_delay(id,create_time) VALUES (1,now()) returning create_time;
create_time
---------------------
2023-02-09 20:25:58
(1 row)
INSERT 0 1
在备库查询表 test_delay 数据, 刚开始查不到,一分钟后可以查出来
postgres=# SELECT now(),create_time FROM test_delay;
now | create_time
-------------------------------+---------------------
2023-02-09 20:26:58.085587+08 | 2023-02-09 20:25:58
(1 row)
从以上时间看出正好相差一分钟,也就是说主库插人这条数据后,过了1分钟左右备库才能查询到这条数据。
接着模拟数据误删场景,假如由于误删了这张表数据 ,是否能在备库找回数据?主库上删除这张数据,如下所示:
postgres=# delete from test_delay;
DELETE 1
尽管主库删除了此表数据,但从库该表数据还存在,如下所示:
postgres=# SELECT now(),create_time FROM test_delay;
now | create_time
-------------------------------+---------------------
2023-02-09 20:28:03.173541+08 | 2023-02-09 20:25:58
(1 row)
这样,可以在延迟时间窗口内将表 test_delay 的表数据进行备份,再导人到主库,从而找回误删除的表。recovery_min_apply_delay 参数设置值过大会使备库的 pg_wal 日志因保留过多的WAL 日志文件而占用较大硬盘空间,因此设置此参数时需要考虑 pg_wal 录可用空间大小,当然,如果设置得太小,留给恢复的时间窗口太短可能起不到 数据恢复的用途。
3 recovery_min_apply_delay 参数对同步复制的影响
recovery_min_apply_delay 参数对同步复制影响如何?大家知道同步复制 synchronous_commit 参数需配置成 on 或者 remote_apply, on 选项意思是主库上提交的事务后会等待备库接收 WAL 日志流并写人 WAL 日志文件后再向客户端返回成功, emote_apply 更进一步,
主库上提交的事务后会等待备库接收 WAL 日志流井写入 WAL 日志文件同时应用完成 WAL 日志流后再向客户端返回成功。
这里对延迟备库场景下 synchronous_commit 配置为 on和remote_apply 的差异进行测试:
场景一: synchronous_commit 配置为 on 和 recovery_min_apply_delay 配置成1分钟
synchronous_ commit 参数调整完后需要执行 pg_ct! reload 重新载人配置使参数生效,同时 recovery_min_apply_delay 置调整后需要重启备库使配置生效。
测试前先在主库上清空表 test_delay 数据,之后在主库上插入一条数据,如下所示:
postgres=# INSERT INTO test_delay(id,create_time) VALUES (1,now()) returning create_time;
create_time
---------------------
2023-02-09 20:31:29
(1 row)
INSERT 0 1
之后在备库上查询这条记录,依然需要一分钟之后这条数据才能查询到,如下所示:
postgres=# SELECT now(),create_time FROM test_delay;
now | create_time
-------------------------------+---------------------
2023-02-09 20:32:31.252722+08 | 2023-02-09 20:31:29
(1 row)
也就是说延迟备库场景, synchronous_commit 配置为 on 时和异步流复制一致。
场景二: synchronous_commit 配置为 remote_apply ,同时 recovery_min_apply_delay 配置成1分钟
主库上执行以下 SQL ,向 test_delay 表中插入一条数据,如下所示:
postgres=# INSERT INTO test_delay(id,create_time) VALUES (1,now()) returning create_time;
--注意这条命令被阻塞
这时发现 SQL 处于阻塞状态,我们把 SQL 计时器打开,看看等了多久,主库上再插入一条数据,如下所示:
postgres=# \timing
Timing is on.
postgres=# INSERT INTO test_delay(id,create_time) VALUES (1,now()) returning create_time;
create_time
---------------------
2023-02-09 20:34:22
(1 row)
INSERT 0 1
Time: 60018.038 ms (01:00.018)
以上看出, SQL 执行时间为 60 秒,一条普通的 INSERT 语句需要执行 60 秒的原因,根据 synchronous_commit 参见 remote_apply 选项的解释,因为主库提交 INSERT 语句后,会等待同步备库接收这条 INSERT 语句的 WAL 日志流并且写入备库 WAL 日志文件,同时备库完成应用 WAL 使得这条记录在备库可见后主库才向客户端返回成功,而此时同步备库又设置了 WAL 应用延迟一分钟,了解了这些原理之后,对于以上两个测试场景的差异就很好理解了。
根据以上测试,对于延迟备库场景, synchronous_commit 置为 on 时和异步流复制一致, synchronous_commit 置为 remote_apply 时,主库上所有的写操作将被阻塞一定时间,被阻塞的时间正好是同步备库 recovery_min_apply_delay 参数配置值,因此 synchronous_commit 参数配置为 remote_apply 的同步流复制环境应避免使用延迟备库。