os: ubuntu 16.04
postgresql: 9.6.8
pg_rewind是用于在集簇的时间线分叉以后,同步一个 PostgreSQL 集簇和同一集簇的另一份拷贝的工具。一种典型的场景是在故障切换后让旧的主服务器作为新主机的备机重新上线。
其结果等效于把目标数据目录替换成源数据目录。数据文件中只有更改过的块才会被拷贝,所有其他的文件会被整个拷贝,包括配置文件。pg_rewind比起做一个新的基础备份或者rsync等工具的优势在于,pg_rewind不要求读取集簇中未更改的块。这使得它在数据库很大并且在集簇间只有小部分块不同时速度很快。
pg_rewind检查源集簇和目标集簇的时间线历史来判断它们在哪一点分叉,并且期望在目标集簇的pg_xlog目录中找到 WAL 来返回到分叉点。分叉点可能会在目标时间线、源时间线或者它们的共同祖先上找到。在典型的失效场景中,目标集簇在分叉后很快就被关闭,这不是问题,但是如果目标集簇在分叉后已经运行了很长时间,旧的 WAL 文件可能已经不存在了。在这样的情况下,它们可以被手工从 WAL 归档复制到pg_xlog目录,或者通过配置recovery.conf在启动时取得。pg_rewind的使用并不限于故障切换的场景,例如一个后备服务器可能被提升、运行一些写事务,然后被倒回再次成为一个后备。
当目标服务器在运行了pg_rewind之后第一次启动时,它将进入到恢复模式并且重放源服务器在分叉点之后产生的所有 WAL。如果运行pg_rewind时有些 WAL 在源服务器上找不到,并且因此无法被pg_rewind复制过来,则在目标服务器被启动时必须让这些 WAL 可用。这可以通过在目标数据目录中创建一个recovery.conf文件并且在其中使用一个适当的restore_command来实现。
pg_rewind有两个要求:
1、目标服务器在postgresql.conf中启用了wal_log_hints选项,或者在用initdb初始化集簇时启用了数据校验。目前默认情况下这两者都没有被打开。
2、full_page_writes也必须被设置为on,这是默认的。
postgres=# show wal_log_hints;
wal_log_hints
---------------
on
(1 row)
postgres=# show full_page_writes;
full_page_writes
------------------
on
(1 row)
过程略
postgres=# create table tmp_t0(c0 varchar(100));
CREATE TABLE
postgres=# insert into tmp_t0 (c0) values('100');
INSERT 0 1
postgres=#
postgres=# \d
List of relations
Schema | Name | Type | Owner
--------+--------+-------+----------
public | tmp_t0 | table | postgres
(1 row)
postgres=# select * from tmp_t0;
c0
-----
100
(1 row)
此时将slave 提升为 master
$ /usr/lib/postgresql/9.6/bin/pg_ctl promote -D /data/pg9.6/main/
此时,有两个master。
postgres=# insert into tmp_t0 (c0) values('101');
INSERT 0 1
postgres=# select * from tmp_t0;
c0
-----
100
101
(2 rows)
postgres=# insert into tmp_t0 (c0) values('102');
INSERT 0 1
postgres=# select * from tmp_t0;
c0
-----
100
102
(2 rows)
那这个时候两个master上的数据不一致了,怎样把老的master变为新的master的slaver。
这个时候就需要用到 pg_rewind.
必须干净的关闭,关闭后暂时不能以备节点角色直接拉起来。
# /etc/init.d/postgresql stop
或者
$ /usr/lib/postgresql/9.6/bin/pg_ctl stop -m fast -D /data/pg9.6/main
$ /usr/lib/postgresql/9.6/bin/pg_rewind --target-pgdata=/data/pg9.6/main \
--source-server='host=192.168.56.100 port=5432 user=postgres password=passw0rd dbname=postgres' \
-P
connected to server
servers diverged at WAL position 0/3009BD8 on timeline 1
rewinding from last common checkpoint at 0/3000218 on timeline 1
reading source file list
reading target file list
reading WAL in target
need to copy 52 MB (total source directory size is 69 MB)
53345/53345 kB (100%) copied
creating backup label and updating control file
syncing target data directory
Done!
创建 recovery.conf 文件
$ vi $PGDATA/recovery.conf
#restore_command = 'lz4 -d /data/backup/pgwalarchive/%f.lz4 %p'
recovery_target_timeline = 'latest'
standby_mode = 'on'
primary_conninfo = 'host=192.168.56.100 port=5432 port=5432 user=replicator password=passw0rd dbname=postgres'
启动老的master
# /etc/init.d/postgresql start
或者
$ /usr/lib/postgresql/9.6/bin/pg_ctl start -D /data/pg9.6/main
$ psql
psql (9.6.8)
Type "help" for help.
postgres=#
postgres=# select * from tmp_t0;
c0
-----
100
102
(2 rows)
至此,老的master已经变为新master的slave。
pg_rewind 的工作方式:
其基本思想是从源集簇拷贝所有文件系统级别的改变到目标集簇:
1、以源集簇的时间线历史从目标集簇分叉出来的点之前的最后一个检查点为起点,扫描目标集簇的 WAL 日志。对于每一个 WAL 记录,读取每一个被动过的数据块。这会得到在目标集簇中从源集簇被分支出去以后所有被更改过的数据块列表。
2、使用直接的文件系统访问(–source-pgdata)或者 SQL (–source-server),把所有那些更改过的块从源集簇拷贝到目标集簇。
3、把所有其他诸如pg_clog和配置文件(除了关系文件之外所有的东西)从源集簇拷贝到目标集簇。
4、从源集簇应用 WAL,从失效处创建的检查点开始(严格来说,pg_rewind并不应用 WAL,它只是创建一个备份标签文件,该文件让PostgreSQL从那个检查点开始向前重放所有 WAL)。
可以这样理解,假如所有的文件都需要拷贝,那么和rsync是一样的。
所以 pg_rewind 不会比 rsync 慢。
参考:
http://postgres.cn/docs/9.6/app-pgrewind.html