PostgreSQL 流复制
PostgreSQL数据库异常中止后,数据库刚重启时,会重放停机前最后一个checkpoint点之后的 WAL日志,在把数据库恢复到停机的状态后,自动进入正常的状态,可以接收其他用户的查询和修改。
想象另一个场景:如果A机器上的数据库停止后,把A机器上的数据库整个拷贝到另一台机器B上,在机器B上启动这个数据库时,机器B上的数据库也将做与A机器上数据库重启时相同的事,即重放停止之前最后一个checkpoint点之后的 WAL日志,把数据库推导到停机时的状态。通常数据库完成恢复后会自动进入正常状态,如果有办法让这个数据库不自动进入正常状态,而是让其一直等待新的 WAL日志,如果有新的 WAL日志来,则自动进行重放,直到主库失败后,再让B机器上的数据库进入正常状态,这样B机器上的数据库就成了-个 Standby 数据库,实现了当A机器上的数据库出故障时,B机器上的数据库能接管的功能。
PostgreSQL的流复制是异步的,异步的缺点是Standby上的数据落后于主库上的数据如果使用做读写分离,就会存在数据一致性的问题,这对于一些一致性较高的应用来说是不可接受的。所以 PostgreSQL从9.1版本之后提供了同步流复制的架构。同步复制要求在数据写入 Standby数据库后,事务的commit才返回,所以 Standby库出现问题时,会导致主库被hang住。解决这个问题的方法是启动两个 Standby数据库,这两个 Standby 数据库只要有一个是正常的,就不会让主库hang住。所以在实际应用中,同步流复制,总是有1个主库和2个以上的 Standby 库。
1、一主一备
主库配置
# postgresql.conf
wal_level='replica'
archive_mode='on'
archive_command='test ! -f /home/postgres/archive/%f && cp %p /home/postgres/archive/%f'
max_wal_senders=10 #可以有多少个流复制链接
wal_keep_size=128MB
wal_sender_timeout=60s #流复制发送主机的超时时间。# pg_hba.conf
host replication repuser 0.0.0.0/0 password
完成以上准备工作后,就可以使用pg_basebackup命令行工具在备机机器上生成基础备份了,命令如下:
pg_basebackup -h 192.168.2.204 -p 1622 -U repuser -W -X stream -F p -P -R -D /home/postgres/pgdata -l backup20250207
完成启动备库,此时就建立好主备了。
默认是异步模式 async
图片
注意:只要主库中synchronous_standby_names参数中没有的名字,均为异步
下面我们给他调整成同步模式 sync
主库 添加参数 vim postgresql.conf
#synchronization level
synchronous_commit = on #同步提交
synchronous_standby_names='node2' #同步提交名字
备库 修改参数 vim postgresql.auto.conf,添加到配置文件的最后
application_name=node2
主、备 重启服务
psql -c "select application_name,client_addr,sync_state from pg_stat_replication;"
调整成sync同步模式
pg_stat_replication 试图中 sync_state 的三个值及其含义1. async(异步)从节点以异步模式运行。这意味着主节点不会等待从节点确认事务已经应用,而是直接提交事务。
2. potential(潜在同步)从节点当前处于潜在同步状态。这意味着如果当前的同步从节点失效,这个从节点将被提升为同步从节点。
3. sync(同步)从节点以同步模式运行。这意味着主节点会等待从节点确认事务已经应用后才提交事务。这确保了更高的数据一致性和高可用性。
pg_stat_replication视图主要参数
psql -xc "select * from pg_stat_replication;"
startup连接中,catchup同步中 streaming 同步
sent_lsn 主库传送wal的位置
write_lsn 从库接收wal的位置
flush_lsn 从库同步到磁盘的wal位置
replay_lsn 从库同步到数据库的wal位置
write_lag 延迟
sync_priority 异步,1~x同步越小,同步级别越高
sync_state async 异步,sync同步,potential现在是异步,但可能升级为同步
2、一主两备
优点:前面说到,同步复制要求在数据写入 Standby数据库后,事务的commit才返回,所以 Standby库出现问题时,会导致主库被hang住。启动两个 Standby数据库,这两个 Standby 数据库只要有一个是正常的,就不会让主库hang住。
pg_basebackup -h 192.168.2.204 -p 1622 -U repuser -W -X stream -F p -P -R -D /home/postgres/pgdata -l backup20250207
修改参数 vim postgresql.auto.conf,添加到配置文件的最后
并不需要重启主库,只需要重新装载配置即可
pg_ctl reload
psql -c "select application_name,client_addr,sync_state from pg_stat_replication;"
- 两个备库一个同步以一个异步,我们来shutdown node2.测试能否提交数据
主节点可以正常提交
2) 我们把两个节点都升级为同步模式,修改配置
#synchronization level
synchronous_commit = on #同步提交
synchronous_standby_names='node2,node3' #同步提交名字
可以看到node3 备设置为potential(潜在同步)
此时我们关闭node2,可以看到 node3 由 potential 升级为 sync
数据可以正常写入
b.此时我们关闭node2,node3,主库会等待,只要一个备库正常,即可提交成功
可以看到node2的优先级是1,node3的优先级是2这个优先级是由synchronous_standby_names 参数配置的顺序决定的。目前主数据库与db02处于同步“sync”,而 node3 的状态为potential”,表示它是一个潜在的同步 Standby,当node2损坏时,node3 会切换到同步状态。
psql -c "select application_name,client_addr,sync_priority,sync_state from pg_stat_replication;"
在PG10及以后版本中,引入了 synchronous_standby_names 这种基于** Quorum**的同步复制优选提交的机制。
同步复制支持一个或者更多个同步后备服务器,事务将会等待,直到所有同步后备服务器都确认收到了它们的数据为止。事务必须等待其回复的同步后备的数量由synchronous_standby_names 指定。这个参数还指定一个后备服务器名称及方法(FIRST和ANY)的列表来从列出的后备中选取同步后备。
方法FIRST 指定一种基于优先的同步复制并且让事务提交等待,直到它们的WAL记录被复制到基于优先级选中的所要求数量的同步后备上为止。在列表中出现较早的后备被给予较高的优先级,并且将被考虑为同步后备。其他在这个列表中位置靠后的后备服务器表示可能的同步后备。如果任何当前的同步后备由于任何原因断开连接,它将立刻被下一个最高优先级的后备所替代。
配置1:如果有多台备node1、node2、node3、node4正在运行,事务提交将会等待来其中中至少任意一台备机器的回复,才能提交成功。node4 是一台异步后备,因为它的名字不在该列表中。
synchronous_standby_names='ANY 1 (node1,node2,node3)'
配置2:如果有多台备node1、node2、node3、node4正在运行,事务提交将会等待来其中中至少任意两台备机器的回复,才能提交成功。node4 是一台异步后备,因为它的名字不在该列表中
synchronous_standby_names='ANY 2 (node1,node2,node3)'
配置3:node1,node2 将被选中为同步备库,主库提交事务时,node1,node2 都写入wal才能提交成功。如果 node1,node2 任意一个失效,node3,node4 可以升级为同步备库。
synchronous_standby_names = 'FIRST 2 (node1,node2,node3,node4)'
注意:
如果在提交正在等待确认时主服务器重启,那些正在等待的事务将在主数据库恢复时被标记为完全提交。没有办法确认所有后备服务器已经收到了在主服务器崩溃时所有还未处理的 WAL 数据。某些事务可能不会在后备服务器上显示为已提交,即使它们在主服务器上显示为已提交。我们提供的保证是:在 WAL 数据已经被所有后备服务器安全地收到之前,应用将不会收到一个事务成功提交的显式确认。
判断是否为备库
postgres=# select pg_is_in_recovery();pg_is_in_recovery
-------------------f
f:主库,t:备库
======================================================
文章参考:唐成老师PostgreSQL修炼之道从小工到专家