1. 简介
官方文档关于postgres的HA有多种方式,笔者通过比较之后决定使用流复制(9.x版本以后引入),主要原因是低延迟(默认异步)。为了避免很多处理环境的很多问题,本文使用docker部署,只挂载出数据盘。
2. 热备(流复制)
流复制允许一台后备服务器比使用基于文件的日志传送更能保持为最新的状态。 后备服务器连接到主服务器, 主服务器则在 WAL 记录产生时即将它们以流式传送给后备服务器而不必等到 WAL 文件被填充。
-
主节点(171):
1.1 创建容器
docker run --name postgresPrimary -p 5432:5432 -v <data_path>:/var/lib/postgresql/data -e POSTGRES_PASSWORD=<passwd> -d postgres:9.5.6-alpine
1.2 进入容器,创建一个数据库用户供主从同步使用
# 进入容器 docker exec -it postgresPrimary bash # 进入数据库 bash-4.3# psql -U postgres # 创建用户 postgres=# CREATE ROLE replica login replication encrypted password 'replica'
1.3 修改<data_path>/pg_hba.conf,允许replica用户来同步
host all all 192.168.1.172/32 trust #允许172连接到主服务器 host replication replica 192.168.1.172/32 md5 #允许172使用replica用户来复制
1.4 修改<data_path>/postgresql.conf
listen_addresses = '*' # 监听所有IP archive_mode = on # 允许归档 archive_command = 'cp %p <archive_path>/%f' # 用该命令来归档logfile segment wal_level = hot_standby max_wal_senders = 32 # 这个设置了可以最多有几个流复制连接,差不多有几个从,就设置几个wal_keep_segments = 256 # 设置流复制保留的最多的xlog数目 wal_sender_timeout = 60s # 设置流复制主机发送数据的超时时间 max_connections = 100 # 这个设置要注意下,从库的max_connections必须要大于主库的
1.5 重启postgres
docker restart postgresPrimary
-
备节点(172)
2.1 启动一个临时容器用于拷贝主节点数据
docker run -it -rm -v <data_path>:/var/lib/temp -e POSTGRES_PASSWORD=<passwd> postgres:9.5.6-alpine bash # 拷贝主节点数据 bash-4.3# cd /var/lib/temp bash-4.3# pg_basebackup -h 192.168.1.171 -U replica -D . -X stream -P bash-4.3# exit
2.2 配置<data_path>/recovery.conf
standby_mode = on # 说明该节点是从服务器 primary_conninfo = 'host=192.168.1.171 port=5432 user=replica password=replica' # 主服务器的信息以及连接的用户 recovery_target_timeline = 'latest' # 同步目标是到最新
2.3 配置postgresql.conf
wal_level = hot_standby max_connections = 1000 # 一般查多于写的应用从库的最大连接数要比较大 hot_standby = on # 说明这台机器不仅仅是用于数据归档,也用于数据查询 max_standby_streaming_delay = 30s # 数据流备份的最大延迟时间 wal_receiver_status_interval = 10s # 多久向主报告一次从的状态,当然从每次数据复制都会向主报告状态,这里只是设置最长的间隔时间 hot_standby_feedback = on # 如果有错误的数据复制,是否向主进行反馈
2.4 启动postgres
docker run --name postgresSecondary -p 5432:5432 -v <data_path>:/var/lib/postgresql/data -e POSTGRES_PASSWORD=<passwd> -d postgres:9.5.6-alpine
-
验证部署是否成功
连接主节点数据库,查询是否已经连通:
postgres=# select client_addr,sync_state from pg_stat_replication; client_addr | sync_state ---------------+------------ 192.168.1.172 | async (1 rows)
3. 监控
postgres有自带的一些方法可以监控数据sync的情况,主要有主节点的pg_stat_replication视图,从节点的pg_is_in_recovery()、pg_last_xact_replay_timestamp()、pg_last_xlog_replay_location()等。
-
主节点
1.1 连接主节点,查看pg_stat_replication 中的状态postgres=# select * from pg_stat_replication;
1.2 在master判断recovery的滞后程度,以字节为单位
postgres=# select pg_xlog_location_diff(sent_location, replay_location) from pg_stat_replication; pg_xlog_location_diff ----------------------- 124 postgres=# select pg_xlog_location_diff(sent_location, replay_location) from pg_stat_replication; pg_xlog_location_diff ----------------------- 120
-
备节点
1.1 判断recovery是否处于工作状态,当Streaming Replication在复制的时候,replay_timestamp和pg_last_xlog_replay_location会一直增长。
postgres=# select pg_last_xact_replay_timestamp(); pg_last_xact_replay_timestamp ------------------------------- 2019-01-31 04:23:10.893194+00 (1 row) postgres=# select pg_last_xact_replay_timestamp(); pg_last_xact_replay_timestamp ------------------------------- 2019-01-31 04:23:18.393878+00 (1 row) postgres=# select pg_last_xlog_replay_location(); pg_last_xlog_replay_location ------------------------------ 5/7C6F6850 (1 row) postgres=# select pg_last_xlog_replay_location(); pg_last_xlog_replay_location ------------------------------ 5/7C6FA258 (1 row)
2.2 查看recovery的延时情况
postgres=# SELECT CASE WHEN pg_last_xlog_receive_location() = pg_last_xlog_replay_location() THEN 0 ELSE EXTRACT (EPOCH FROM now() - pg_last_xact_replay_timestamp()) END AS log_delay; # 如果receive和replay是同一个位置,延时为0;否则当前时间减去最后一个事务的时间为延时 log_delay ----------- 0 (1 row)
4. 主从不一致预防与修复
主从不一致基本上都是由于在主库产生了大量xlog,由于postgreSQL在执行事务时,在提交时才发送到备库。由于该事务需要执行的时间过长,超过了checkpoint的默认间隔,所以导致有的xlog还未发送到备库却被remove掉了。
-
一般报错
could not receive data from WAL stream:FATAL: requested WAL segment 0000000*** has already been removed
-
预防
2.1 调整wal_keep_segments的值(无法根治)
将GUC参数wal_keep_segments设大一些,比如设置为2000,而每个segment默认值为16MB,就相当于有32000MB,即大概30多个GB的空间作为缓存空间。
2.2 启用归档
归档,就是将未发送到备库的xlog备份到某个目录下,待重启数据库时再将其恢复到备库中去。
本文做热备的时候就已经启动了归档。
2.3 启用replication slot(pg9.4以后才有, 根本方法)
这个不会造成xlog的丢失。也就是说,在xlog被拷贝之前,不会删除。
- 在主库postgresql.conf中添加
max_replication_slots = 2000
- 在拷贝到备库之前,主库要创建一个slot
postgres=# SELECT * FROM pg_create_physical_replication_slot('node_a_slot'); slot_name | xlog_position -------------+--------------- node_a_slot | postgres=# SELECT * FROM pg_replication_slots; slot_name | slot_type | datoid | database | active | xmin | restart_lsn -------------+-----------+--------+----------+--------+------+------------- node_a_slot | physical | | | f | | (1 row)
- 在备库的recovery.conf文件中添加一行:
primary_slot_name = 'node_a_slot'
- 在主库postgresql.conf中添加
-
修复
3.1 如果有归档,可以用主节点的归档来恢复备节点scp <archive_path>/000000*** root@192.168.1.172:<data_path>/pg_xlog/
3.2 如果没有归档,只能重建备节点(按照上面的热备备节点操作)
参考:
PostgreSQL主从流复制部署
PostgreSQL 9.3 Streaming Replication 状态监控
PostgreSQL:“ FATAL: requested WAL segment00800002A0 has already been removed”