PG主备恢复工具之pg_rewind
集群故障后如何恢复原主机
pg的高可用架构中,主库挂掉后,备库会自动升级为主库继续提供服务,对于原来的主库通常有两种处理方式
- 删掉,重搭新备库。
- 降级为备库,继续服务。
很显然,相比来说第一种不是个很好的方案,而且当数据量比较大时,重搭备库的时间成本太高。
但是因为老的主库挂掉的原因多种多样,甚至有可能是高可用系统的误判,而老主库也有可能是在挂掉之后又重新作为主库启动起来,这个时候降级并重搭流复制关系的操作就有可能失败(新的备库比新主库数据更超前)。
为了解决这种情况,PostgreSQL 引入了pg_rewind工具。
简介
pg_rewind是postgresql主备数据库之同步数据目录的工具。需要目标服务器在postgresql.conf 中允许wal_log_hints,或者在 initdb初始化集群时允许checksums ,目前默认情况下这两者都没有被打开。full_page_writes也必须为on,这是默认的。
1 2 | wal_log_hints=on full_page_writes=on |
有关如何开启cheksum可以参考:https://www.xmmup.com/pg12zhongdepg_checksumsmingling.html
pg_rewind相比pg_basebackup和rsync这样的工具来说,优势是它不需要从源目录拷贝所有的数据文件,而是会对比时间线发生偏离的点,只拷贝变化过的文件,这样对于数据量很大的情况下速度更快。
备库上运行pg_rewind会使得数据库进入恢复状态,备库会从主库读取必要的wal文件,如果源库上因为跑了很长一段时间造成wal丢失,则可以手工从归档目录进行拷贝。
pg_rewind只复制表数据文件中更改的块;所有其他文件都被完整复制,包括配置文件。pg_rewind相对于使用pg_basebackup备份或rsync等工具的优势在于,pg_rewind不需要读取数据库中未更改的块。这使得在数据库很大且之间只有一小部分块不同的情况下,速度会快得多。
pg_rewind是用于在集簇的时间线分叉以后,同步一个 PostgreSQL 集簇和同一集簇的另一份拷贝的工具。一种典型的场景是在失效后让一个旧的主服务器重新上线,同时有一个后备机跟随着新的主机。
成功回放后,目标数据目录的状态类似于源数据目录的基本备份。与进行新的基本备份或使用rsync等工具不同,pg_rewind不需要比较或复制集群中未更改的关系块。仅复制现有关系文件中更改的块;所有其他文件(包括新的关系文件、配置文件和WAL段)都将被完整复制。因此,当数据库很大并且集群之间只有一小部分块不同时,倒带操作比其他方法要快得多。
pg_rewind检查源集簇和目标集簇的时间线历史来判断它们在哪一点分叉,并且期望在目标集簇的pg_wal
目录中找到 WAL 来返回到分叉点。分叉点可能会在目标时间线、源时间线或者它们的共同祖先上找到。在典型的失效场景中,目标集簇在分叉后很快就被关闭,这不是问题,但是如果目标集簇在分叉后已经运行了很长时间,旧的 WAL 文件可能已经不存在了。在这样的情况下,您可以手动将它们从WAL存档复制到pg_wal
目录,或使用-c
选项运行pg_rewind以自动从WAL存档检索它们。pg_rewind的使用并不限于失效的场景,例如一个后备服务器可能被提升、运行一些写事务,然后被倒回再次成为一个后备。
在运行pg_rewind之后,需要完成WAL重放以使数据目录处于一致状态。当目标服务器再次启动时,它将进入归档恢复,并从分歧点之前的最后一个检查点重放源服务器中生成的所有 WAL。当pg_rewind被运行时有某些 WAL 在源服务器上不可用,并且因此无法被pg_rewind会话所复制,则在目标服务器被启动时必须让这些 WAL 可用。 这可以通过在目标数据目录中创建一个recovery.signal
文件并且在postgresql.conf
中配置适合的restore_command来实现。
pg_rewind要求目标服务器在postgresql.conf
中启用了wal_log_hints选项,或者在用initdb初始化集簇时启用了数据校验。目前默认情况下这两者都没有被打开。full_page_writes也必须被设置为on
,这是默认的。
警告
如果在处理时pg_rewind失败,则目标的数据目录很可能不在可恢复的状态。在这种情况下,推荐创建一个新的备份。
由于 pg_rewind 完全从源复制配置文件,因此可能需要在重新启动目标服务器之前更正用于恢复的配置,特别是当目标服务器作为源的备用服务器重新引入时。 如果在倒带操作完成后重新启动服务器但未配置恢复,则目标可能会再次与主服务器分离。
如果pg_rewind发现它无法直接写入的文件,它将立刻失败。例如当源服务器和目标服务器为只读的SSL密钥及证书使用相同的文件映射,就会发生这种情况。如果在目标服务器上存在这样的文件,推荐在运行pg_rewind之前移除它们。在做了rewind之后,一些那样的文件可能已经被从源服务器拷贝,这样就有必要移除已经拷贝的数据并且恢复到rewind之前使用的链接集合。
参数wal_log_hints (boolean)
当这个参数为on
时,PostgreSQL服务器一个检查点之后页面被第一次修改期间把该磁盘页面的整个内容都写入 WAL,即使对所谓的提示位做非关键修改也会这样做。
如果启用了数据校验和,提示位更新总是会被 WAL 记录并且这个设置会被忽略。你可以使用这个 设置测试如果你的数据库启用了数据校验和,会有多少额外的 WAL 记录发生。
这个参数只能在服务器启动时设置。默认值是off
。
语法
1 | pg_rewind [option...] { -D | --target-pgdata } directory { --source-pgdata=directory | --source-server=connstr |
pg_rewind接受下列命令行参数:
-D *
directory*
--target-pgdata=*
directory*
这个选项指定要与源数据目录同步的目标数据目录。在运行pg_rewind之前目标服务器必须被干净地关闭。
--source-pgdata=*
directory*
指定要和目标服务器同步的源服务器的数据目录的文件系统路径。这个选项要求源服务器必须被干净地关闭。
--source-server=*
connstr*
指定一个 libpq 连接串用于连接要与目标服务器同步的源PostgreSQL服务器。 连接必须是常规(非复制)连接,角色具有足够权限执行源服务器上pg_rewind使用的函数(详请参阅备注部分)或超级用户角色。这个选项要求源服务器正在运行且不处于恢复模式。
-R
--write-recovery-conf
创建
standby.signal
并将连接设置附加到输出目录中的postgresql.auto.conf
中。--source-server
对于此选项是必需的。-n
--dry-run
做除了实际修改目标目录之外的其他所有事情。
-N
--no-sync
默认情况下,
pg_rewind
将等待所有文件安全地写入磁盘。 此选项会导致pg_rewind
不等待即可返回,这更快,但意味着后续操作系统崩溃会使同步数据目录损坏。通常情况,此选项可用于测试,但不应使用于生产安装。-P
--progress
启用进度报告。在从源集簇拷贝数据时,打开这个选项将会发送一个近似的进度报告。
-c
--restore-target-wal
如果在
pg_wal
目录中不再可用这些文件,请使用在目标群集配置中定义的restore_command
从WAL存档中检索WAL文件。--debug
打印冗长的调试输出,这主要对于调试pg_rewind的开发者有用。
--no-ensure-shutdown
pg_rewind 要求目标服务器在倒带之前彻底关闭。 默认情况下,如果目标服务器没有完全关闭,pg_rewind 会以单用户模式启动目标服务器,先完成崩溃恢复,然后将其停止。 通过传递这个选项,如果服务器没有完全关闭,pg_rewind 会跳过这个并立即出错。 在这种情况下,用户应该自己处理这种情况。
-V
--version
显示版本信息然后退出。
-?
--help
显示帮助然后退出。
权限
当使用在线群集作为源执行pg_rewind时,具有充足权限来执行pg_rewind在源群集上使用的函数的角色可以用来代替超级用户。 这里介绍如何创建这样的角色,在这里命名rewind_user
:
1 2 3 4 5 | CREATE USER rewind_user LOGIN; GRANT EXECUTE ON function pg_catalog.pg_ls_dir(text, boolean, boolean) TO rewind_user; GRANT EXECUTE ON function pg_catalog.pg_stat_file(text, boolean) TO rewind_user; GRANT EXECUTE ON function pg_catalog.pg_read_binary_file(text) TO rewind_user; GRANT EXECUTE ON function pg_catalog.pg_read_binary_file(text, bigint, bigint, boolean) TO rewind_user; |
当使用近期升级的在线群集作为源执行pg_rewind时,必须在升级后执行CHECKPOINT
以便其控制文件反映最新的时间线信息, pg_rewind使用这些信息检查目标群集是否可以使用指定的源群集倒回。
如何工作
其基本思想是从源集簇拷贝所有文件系统级别的改变到目标集簇:
- 以源集簇的时间线历史从目标集簇分叉出来的点之前的最后一个检查点为起点,扫描目标集簇的 WAL 日志。对于每一个 WAL 记录,读取每一个被动过的数据块。这会得到在目标集簇中从源集簇被分支出去以后所有被更改过的数据块列表。如果某些 WAL 文件 不再可用,请尝试使用
-c
选项重新运行 pg_rewind 以在 WAL 存档中搜索丢失的文件。 - 使用直接的文件系统访问(
--source-pgdata
)或者 SQL (--source-server
),把所有那些更改过的块从源集簇拷贝到目标集簇。 关系文件现在的状态相当于源和目标的WAL时间线偏离点之前最后一个完成的检查点的时刻加上偏离点之后目标上更改的任何块的源上的当前状态。 - 将所有其他文件,包括新的关系文件、WAL段、
pg_xact
和配置文件,从源集群复制到目标集群。与基本备份类似,从源集群复制的数据中省略了目录pg_dynshmem/
、pg_notify/
、pg_replslot/
、pg_serial/
、pg_snapshots/
、pg_stat_tmp/
以及pg_subtrans/
的内容。文件backup_label
、tablespace_map
、pg_internal.init
、postmaster.opts
以及postmaster.pid
,以及任何以pgsql_tmp
开始的文件或目录都会被忽略。 - 创建一个
backup_label
文件,在故障转移时创建的检查点处开始WAL重放,并将pg_control
文件配置为最小一致性LSN,该LSN定义为从活动源回放时的pg_current_wal_insert_lsn()
结果,或从停止的源回放时的最后一个检查点LSN。 - 启动目标时,PostgreSQL将重放所有必需的WAL,从而使数据目录处于一致状态。
为了在PostgreSQL 中实现文件级别同步数据的功能,pg_rewind 主要进行了如下的处理步骤:
- 在目的集群中找到源集群和目的集群的分叉点之前的最近一次checkpoint 点。这样相当于找到了在两个副本数据产生不同前的最后一个一致性位点。目的集群在这个位点之后所有的表数据变化都记录在这个位点之后的WAL 日志中。
- pg_rewind 会将目的集群这些变化的数据页从源集群复制过来。这里会有2种方式:
- 使用文件系统方式拷贝
- 使用 libpq 建立连接的方式拷贝
总结来讲:pg_rewind 可以快速找到两个集群数据开始分叉的点,然后找到目的集群从该点之后的数据变化,通过拷贝源集群的对应数据页,再通过应用源集群的WAL 日志达到数据一致。