Postgresql 主备原理及部署

张彤 2022年03月31日 2,066次浏览

[toc]

Postgresql主从备份介绍

Postgresql中可以实现Oralce中类似standby热备功能的主从复制功能。

日志传输种类

PostgreSQL日志传送的方法有两种:

  1. 基于文件(base-file)的传送方式
    基于文件(base-file)的传送方式,这种方式是PostgreSQL9.0之前就提供的方法.
    也就是服务器写完一个WAL日志文件后,才把WAL日志文件拷贝到standby数据库上去应用.

  2. 流复制(streamingreplication)的方式
    流复制的方式是PostgreSQL提供的一种服务器间的数据复制方式
    这个方法就是事务提交后,主服务器则在WAL记录产生时即将它们以流式传送给后备服务器而不必等到WAL文件被填充
    这比基本文件的日志传送方法有更低的数据延迟
    物理流复制有如下优点:

    1. 延迟极低,不怕大事务
    2. 支持断点续传,备机宕机,重启后可以从断点开始传输数据
    3. 支持多副本,一主多备
    4. 配置简单
    5. 备库与主库物理完全一致,并支持只读。备机可以做读服务

日志复制步骤

流复制主要有两步完成

  1. 事务commit后,日志在主库写入wal日志,还需要根据配置的日志同步级别,等待从库反馈的接收结果。
  2. 主库通过日志传输进程将日志块传给从库,从库接收进程收到日志开始回放,最终保证主从数据一致性。

流复制同步级别

在文postgresql.conf中,通过修改synchronous_commit 参数来控制主从复制的事务级别。

  • remote_apply
    事务commit或rollback时,等待其redo在primary、以及同步standby(s)已持久化,并且其redo在同步standby(s)已apply。

  • on
    事务commit或rollback时,等待其redo在primary、以及同步standby(s)已持久化。

  • remote_write
    事务commit或rollback时,等待其redo在primary已持久化; 其redo在同步standby(s)已调用write接口(写到 OS, 但是还没有调用持久化接口如fsync)。

  • local
    事务commit或rollback时,等待其redo在primary已持久化;

  • off
    事务commit或rollback时,等待其redo在primary已写入wal buffer,不需要等待其持久化;

不同的事务同步级别对应的数据安全级别越高,对应的对性能影响也就越大。上述从上至下安全级别越来越低。

主从部署

部署流程

PostgreSQL物理流复制大致过程为:
主库

  1. PG软件安装
  2. postgresql.conf参数配置
  3. pg_hba.conf配置

备库

  1. pg_basebackup方式部署备库
  2. 配置recovery.conf
  3. 备库与主库物理完全一致,并支持只读

环境准备

  1. 服务器准备
服务器IPPostgresql Port
主机192.168.10.1115432
备机192.168.10.1215432
  1. 环境准备
依赖项目依赖名称
操作系统CentOS-7-x86_64
Postgresql版本PostgreSQL 10.20 on x86_64-pc-linux-gnu
数据安装目录/mnt/pgdata/
  • 记得在防火墙上把端口打开

主机部署

  1. 添加复制账户
# 启动pg10
systemctl start postgresql-10
# 登陆用户
su - postgres
psql -p 15432
# 创建用户
create user repuser with login replication password 'Aa1234'; 
  1. 修改pg_hba.conf文件,添加复制权限
vim /mnt/pgdata/pg_hba.conf

在最下方追加内容

# standby setting
host    replication    repuser        192.168.10.11/32         md5

host    replication    repuser        192.168.10.12/32         md5

host      all          postgres          192.168.10.11/32             trust

host      all          postgres          192.168.10.12/32             trust
  1. 修改postgresql.conf文件
vim /mnt/pgdata/postgresql.conf

修改相关项的值

  • 这里需要注意的是,archive_mode = on,归档是否开启是要看情况而定的,如果你的主库是新库,没有数据灌入,那么没有必要开启归档。
    如果你的主库是生产在运的,必须开启归档,因为如果在backup流式同步到备机需要一定的时间,在这段时间内,wal日志默认1GB,如果不开归档,会覆盖掉主机上的wal日志,备机开启的时候,会报找不到对应的wal日志错误。
  • 如果你的主机对恢复没有很强的要求,建议强制归档时间设置成60秒archive_timeout = 60,如果该参数为0,开销会增加。
archive_mode = on 

archive_timeout = 60

max_wal_senders = 10

wal_level = replica

wal_log_hints = on

wal_keep_segments = 10

wal_receiver_status_interval = 5s

hot_standby_feedback = on

参数含义

参数含义
max_wal_senders表示来自后备服务器或流式基础备份客户端的并发连接的最大数量
wal_level表示日志级别,对于流复制,它的值应设置为replica
wal_log_hints在PostgreSQL服务器一个检查点之后页面被第一次修改期间,把该磁盘页面的整个内容都写入WAL,即使对所谓的提示位做非关键修改也会这样做
wal_keep_segments指定在后备服务器需要为流复制获取日志段文件的情况下,pg_wal(PostgreSQL9.6 以下版本的是pg_xlog)目录下所能保留的过去日志文件段的最小数目;
wal_receiver_status_interval指定在后备机上的 WAL接收者进程向主服务器或上游后备机发送有关复制进度的信息的最小周期;
hot_standby_feedback指定一个热后备机是否将会向主服务器或上游后备机发送有关于后备机上当前正被执行的查询的反馈
  1. 重启主节点
systemctl restart postgresql-10
  1. 添加复制槽
-- 创建
select * from pg_create_physical_replication_slot('postgresql_node_10_11');
select * from pg_create_physical_replication_slot('postgresql_node_10_12');

-- 查看复制槽
select * from pg_replication_slots;
  • 复制槽replicationslot 的作用
  1. 在流复制中,当一个备节点断开连接时,备节点通过hot_standby_feedback提供反馈数据数据会丢失
    当备节点重新连接时,它可能因为被主节点发送清理记录而引发查询冲突
    复制槽即使在备节点断开时仍然会记录下备节点的xmin(复制槽要需要数据库保留的最旧事务ID)值,从而确保不会有清理冲突。
  2. 当一个备节点断开连接时,备节点需要的WAL文件信息也丢失了
    如果没有复制槽,当备节点重连时,可能已经丢弃了所需要的WAL文件,因此需要完全重建备节点
    而复制槽确保这个节点保留所有下游节点需要的WAL文件。

备机部署

  1. 停止pg-10 服务
systemctl stop postgresql-10
  1. 删除数据目录中的文件
# 这里的 $PGDATA 一定要配置正确
cd $PGDATA
rm –rf *

  1. 拉取主机的物理数据
pg_basebackup -Xs -d "hostaddr=192.168.10.11 port=15432 user=repuser password=Aa1234" -D $PGDATA -v -Fp
  • pg_basebackup
    -Xs表示复制方式是流式的(stream),这种方式不会复制在此次备份开始前,已经归档完成的WAL文件
    -v 表示打印详细信息
    –Fp表示复制结果输出位普通(plain)文件
  1. 修改备节点的postgresql.conf 文件
vim postgresql.conf
# 修改以下项
hot_standby = on
  1. recovery.conf配置
    将样例复制到pgdata中
cp /usr/pgsql-10/share/recovery.conf.sample $PGDATA/recovery.conf

修改recovery.conf文件

vim /mnt/pgdata/recovery.conf

修改相关的配置项

recovery_target_timeline = 'latest'

standby_mode = on

primary_conninfo = 'host=192.168.10.11 port=15432 user=repuser password=Aa1234'

primary_slot_name = 'postgresql_node_10_02'

trigger_file = 'tgfile'
  • 参数含义
参数含义
recovery_target_timeline表示恢复到数据库时间线的上的什么时间点,这里设置为latest,即最新;
standby_mode表示是否将PostgreSQL服务器作为一个后备服务器启动,这里设置为on,即后备模式
primary_conninfo指定后备服务器用来连接主服务器的连接字符串
primary_slot_name指定通过流复制连接到主服务器时使用一个现有的复制槽来控制上游节点上的资源移除
trigger_file指定一个触发器文件,该文件的存在会结束后备机中的恢复,使它成为主机

最后,重新启动备用节点

systemctl restart postgresql-10

主备环境检测

在主节点上新建表格,并插入数据

create table tb_test(
    id serial primary key ,
    name text,
    age int
);

insert into tb_test(name,age)
values ('张彤',123);

在备用节点上监测

select * from tb_test;
  • 需要注意的是,备用节点数据库在未提升为主节点前,都是只读的,不能修改。vacuum,analyzeexecute 都不能

至此,主从部署完成,下面介绍故障时,如何主备切换。


主备环境切换

当主节点出现故障,为了不耽误生产,需要紧急启动备份数据库成为主节点数据库。在原主节点服务器排障完毕后,降为备用节点,即主备切换

  1. 关闭主节点,造成主节点故障

    # 主节点操作
    systemctl stop postgresql-10
    
  2. 启动备节点,使之成为新的主节点

# 备用节点操作
pg_ctl promote -D $PGDATA
  • 注意,备用节点一旦执行此命令,意味着备用节点正式升级为主节点了
  • 在原主节点恢复后,就不要在原主节点上做任何操作了,否则原主节点由于checkpoint问题,和现主节点时间线不同步,无法作为备用节点啦
  1. 查看新主节点的状态

    # 原备用节点,现主节点操作
    postgres=#  pg_controldata | grep cluster
    
    Database cluster state:              in production
    
    # 插入数据测试
    insert into tb_test(name,age)
    values ('王五',124);
    
    # 这个时候,新的主节点已经可以插入数据了。
    
    
  2. 在停止的旧主节点上执行恢复数据的操作

    # 原主节点操作
    pg_rewind --target-pgdata $PGDATA--source-server='host=192.168.10.12 port=15432 user=postgres dbname=postgres' -P
    
  3. 重新配置新备节点的recovery.conf

recovery_target_timeline = 'latest'

standby_mode = on

primary_conninfo = 'hostaddr=192.168.10.12 port=15432 user=repuser password=Aa1234'

primary_slot_name = 'postgresql_node_10_01'
  1. 重启新的备用节点

    systemctl restart postgresql-10
    
  2. 新的备份节点上测试插入

    insert into tb_test(name,age)
    values ('赵四',125);
    ERROR:  cannot execute INSERT in a read-only transaction
    # 已经无法插入,主备已经完全转换