searchusermenu
  • 发布文章
  • 消息中心
点赞
收藏
评论
分享
原创

PostgreSQL一主多备实践(二)

2023-10-30 01:57:51
3
0

1. 一主多备同步流复制

同步复制下,如果因主库发生临时故障激活了其中一个备库,要想把原主库转换成新主库的备库,仍然需要用pg_rewind处理一下才行,这是因为虽然是同步复制,但并不是把主库的WAL日志完全同步地传输到备库,同步只是到事务提交时才保证其已经传输到了备库,一些未提交事务的WAL日志可能还没有传输到备库,因此激活备库时,还是会丢失一些未提交的WAL日志。当然对于用户来说,未提交事务的WAL日志丢失,并不会导致用户数据的丢失。

1.1. 同步流复制的配置

  1. synchronous_standby_names = 'slave0, slave1, slave2'

synchronous_standby_names可以指定多个备库的名称。slave0, slave1, slave2都是同步流复制的备库。只有第一个slave0是同步备库,其它的库是潜在的同步备库(potential)。

即只要WAL日志传递到第一个备库slave0,事务commit就可以返回了。当第一个备库s1出现问题时,第二个备库s2才会提升为同步备库

  1. 设置多个同步备库:synchronous_standby_names='2 (s1,s2,s3)'

WAL日志必须传到前两个备库“s1”和“s2”,事务commit才可以返回。所以之前版本中的配置 's1, s2, s3' 相当于 '1(s1, s2, s3)' 。

  1. synchronous_standby_names='ANY 2 (s1, s2, s3)'

只要WAL日志传到了任意两个备库,事务commit就可以返回了

synchronous_standby_names只需要pg_ctl reload就可以刷新配置,不需要重启

  1. primary_conninfo
primary_conninfo = 'application_name = slave0 host=10.0.0.3 port=xxxx user=repl password=xxxxxxxx'
    1. application_name = slave0:与主库的synchronous_standby_names参数对应
    2. host=10.0.0.3 port=5432:主库的ip地址和端口
    3. user=repl password=xxxxxxx:具有流复制权限的账号,密码
    4. 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的备库

测试:

  1. 往.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)
  1. 除了.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 掉了。

一般有两种解决方法:

  1. 调大参数 wal_keep_segments 的值

不能根本解决问题,当某条事务需要插入很多数据的时候,有可能还是会出现该问题。

  1. 开启了归档日志的情况下,从归档目录找到该wal日志,传到备库pg_wal下就可以了。
  2. 未开启归档日志的情况下,只能使用pg_basebackup重建备库,代价较大,所以需要开启日志归档
0条评论
0 / 1000
Toliatong
4文章数
0粉丝数
Toliatong
4 文章 | 0 粉丝
原创

PostgreSQL一主多备实践(二)

2023-10-30 01:57:51
3
0

1. 一主多备同步流复制

同步复制下,如果因主库发生临时故障激活了其中一个备库,要想把原主库转换成新主库的备库,仍然需要用pg_rewind处理一下才行,这是因为虽然是同步复制,但并不是把主库的WAL日志完全同步地传输到备库,同步只是到事务提交时才保证其已经传输到了备库,一些未提交事务的WAL日志可能还没有传输到备库,因此激活备库时,还是会丢失一些未提交的WAL日志。当然对于用户来说,未提交事务的WAL日志丢失,并不会导致用户数据的丢失。

1.1. 同步流复制的配置

  1. synchronous_standby_names = 'slave0, slave1, slave2'

synchronous_standby_names可以指定多个备库的名称。slave0, slave1, slave2都是同步流复制的备库。只有第一个slave0是同步备库,其它的库是潜在的同步备库(potential)。

即只要WAL日志传递到第一个备库slave0,事务commit就可以返回了。当第一个备库s1出现问题时,第二个备库s2才会提升为同步备库

  1. 设置多个同步备库:synchronous_standby_names='2 (s1,s2,s3)'

WAL日志必须传到前两个备库“s1”和“s2”,事务commit才可以返回。所以之前版本中的配置 's1, s2, s3' 相当于 '1(s1, s2, s3)' 。

  1. synchronous_standby_names='ANY 2 (s1, s2, s3)'

只要WAL日志传到了任意两个备库,事务commit就可以返回了

synchronous_standby_names只需要pg_ctl reload就可以刷新配置,不需要重启

  1. primary_conninfo
primary_conninfo = 'application_name = slave0 host=10.0.0.3 port=xxxx user=repl password=xxxxxxxx'
    1. application_name = slave0:与主库的synchronous_standby_names参数对应
    2. host=10.0.0.3 port=5432:主库的ip地址和端口
    3. user=repl password=xxxxxxx:具有流复制权限的账号,密码
    4. 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的备库

测试:

  1. 往.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)
  1. 除了.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 掉了。

一般有两种解决方法:

  1. 调大参数 wal_keep_segments 的值

不能根本解决问题,当某条事务需要插入很多数据的时候,有可能还是会出现该问题。

  1. 开启了归档日志的情况下,从归档目录找到该wal日志,传到备库pg_wal下就可以了。
  2. 未开启归档日志的情况下,只能使用pg_basebackup重建备库,代价较大,所以需要开启日志归档
文章来自个人专栏
PostgreSQL使用
4 文章 | 1 订阅
0条评论
0 / 1000
请输入你的评论
0
0