1. 一主多备同步流复制
同步复制下,如果因主库发生临时故障激活了其中一个备库,要想把原主库转换成新主库的备库,仍然需要用pg_rewind处理一下才行,这是因为虽然是同步复制,但并不是把主库的WAL日志完全同步地传输到备库,同步只是到事务提交时才保证其已经传输到了备库,一些未提交事务的WAL日志可能还没有传输到备库,因此激活备库时,还是会丢失一些未提交的WAL日志。当然对于用户来说,未提交事务的WAL日志丢失,并不会导致用户数据的丢失。
1.1. 同步流复制的配置
synchronous_standby_names = 'slave0, slave1, slave2'
synchronous_standby_names可以指定多个备库的名称。slave0, slave1, slave2都是同步流复制的备库。只有第一个slave0是同步备库,其它的库是潜在的同步备库(potential)。
即只要WAL日志传递到第一个备库slave0,事务commit就可以返回了。当第一个备库s1出现问题时,第二个备库s2才会提升为同步备库。
- 设置多个同步备库:
synchronous_standby_names='2 (s1,s2,s3)'
WAL日志必须传到前两个备库“s1”和“s2”,事务commit才可以返回。所以之前版本中的配置 's1, s2, s3' 相当于 '1(s1, s2, s3)' 。
synchronous_standby_names='ANY 2 (s1, s2, s3)'
只要WAL日志传到了任意两个备库,事务commit就可以返回了
synchronous_standby_names只需要pg_ctl reload就可以刷新配置,不需要重启
primary_conninfo
primary_conninfo = 'application_name = slave0 host=10.0.0.3 port=xxxx user=repl password=xxxxxxxx'
- application_name = slave0:与主库的synchronous_standby_names参数对应
- host=10.0.0.3 port=5432:主库的ip地址和端口
- user=repl password=xxxxxxx:具有流复制权限的账号,密码
- 在
postgresql.auto.conf
文件中同样需要配置primary_conninfo
,否则还是异步复制
1.2. 测试同步流复制
创建一主两备实例
主:10.0.0.2
sync:10.0.0.3
potential:10.0.0.4
postgres=# select pid,state,client_addr,sync_priority,sync_state from pg_stat_replication;
pid | state | client_addr | sync_priority | sync_state
-------+-----------+-------------+---------------+------------
19681 | streaming | 10.0.0.4 | 2 | potential
29433 | streaming | 10.0.0.3 | 1 | sync
先关掉一台机器10.0.0.3
postgres=# select pid,state,client_addr,sync_priority,sync_state from pg_stat_replication;
pid | state | client_addr | sync_priority | sync_state
-------+-----------+-------------+---------------+------------
19681 | streaming | 10.0.0.4 | 2 | sync
10.0.0.4升级成同步备库sync
往主库中插入数据,成功
postgres=# insert into user_account values (2, 'Vincy', 42);
INSERT 0 1
关闭最后一个备库,再往主库中插入数据
postgres=# delete from user_account where id = 3;
....卡住了
-- 启动10.0.0.4
-- 看主库,插入成功
INSERT 0 1
--查看同步状态
postgres=# select pid,state,client_addr,sync_priority,sync_state from pg_stat_replication;
pid | state | client_addr | sync_priority | sync_state
-------+-----------+-------------+---------------+------------
31606 | streaming | 10.0.0.4 | 2 | sync
启动10.0.0.3,可以看到数据已经完成了同步,并且10.0.0.3升级为了sync,而先启动的10.0.0.4降级为potential
postgres=# select pid,state,client_addr,sync_priority,sync_state from pg_stat_replication;
pid | state | client_addr | sync_priority | sync_state
-------+-----------+-------------+---------------+------------
31606 | streaming | 10.0.0.4 | 2 | potential
32092 | streaming | 10.0.0.3 | 1 | sync
所以在一主多备的同步流复制中,会保持同步备机的优先级,按照synchronous_standby_names的参数列表排序,优先级由高到底,例如synchronous_standby_names = 's1, s2',则s1的同步优先级最高,s1宕机后再被作为备库启动,会直接取代当前的sync备库。
2. 级联复制测试
三台机器:10.0.0.3(主) --> 10.0.0.2(主/备) --> 10.0.0.4(备)
.3是主库,.2是.3的备库,.4是.3的备库
测试:
- 往.3中写入数据,.2和.4都可以同步到数据
.3执行
CREATE TABLE user_account (id int primary key not null,
name char(32) not null,
age int);
.2机器:
postgres=# \dt
List of relations
Schema | Name | Type | Owner
--------+--------------+-------+-------
public | user_account | table | postgres
(1 row)
.3机器:
postgres=# \dt
List of relations
Schema | Name | Type | Owner
--------+--------------+-------+-------
public | user_account | table | postgres
(1 row)
- 除了.3主库,其他两个备库都不能写数据
postgres=# insert into user_account values (1, 'Tobi', 24);
ERROR: cannot execute INSERT in a read-only transaction
3. 问题
3.1. 备库失去连接
在一主一备的状态下加了一个备库2,然后一起启动的时候发现备库1可以启动,但是与主库失去了联系,不再是主库的备库了。
主库查询状态:发现备库1查不到了
select application_name, state, sync_priority, sync_state from pg_stat_replication;
application_name | state | sync_priority | sync_state
------------------+-----------+---------------+------------
walreceiver | streaming | 1 | sync
报错:某一个wal日志被移走
could not receive data from WAL stream:FATAL: requested WAL segment 000000080000000000000003 has already been removed
推测是因为 PostgreSQL 在执行事务过程中,由于该事务需要执行的时间过长,超过了 checkpoint 的默认间隔,所以导致有的 wal日志 还未发送到备库却被 remove 掉了。
一般有两种解决方法:
- 调大参数
wal_keep_segments
的值
不能根本解决问题,当某条事务需要插入很多数据的时候,有可能还是会出现该问题。
- 开启了归档日志的情况下,从归档目录找到该wal日志,传到备库pg_wal下就可以了。
- 未开启归档日志的情况下,只能使用
pg_basebackup
重建备库,代价较大,所以需要开启日志归档。