背景
两节点HA架构,如何做到跨机房RPO=0(可靠性维度)?同时RTO可控(可用性维度)?
半同步是一个不错的选择。
1、当只挂掉一个节点时,可以保证RPO=0。如下:
主 -> 从(挂)
主(挂) -> 从
2、当一个节点挂掉后,在另一个节点恢复并开启同步模式前,如果在此期间(当前)主节点也挂掉,(虽然此时从库活了(但由于还未开启同步模式)),则RPO>0。 如下:
主(挂) -> 从(OPEN,但是之前从挂过,并且还还未转换为同步模式)
与两个节点同时挂掉一样,RPO>0
3、如何保证RTO时间可控?
我们知道,在同步模式下,事务提交时需要等待sync STANDBY的WAL复制反馈,确保事务wal落多个副本再反馈客户端(从动作上来说,先持久化主,然后同步给sync从,并等待sync从的WAL 同步位点的反馈),当STANDBY挂掉时,等待是无限期的,所以两节点的同步复制,无法兼顾可用性(RTO)。那么怎么兼顾可用性呢?
可以对(pg_stat_activity)等待事件的状态进行监测,如果发现同步事务等待超过一定阈值(RTO阈值),则降级为异步模式。
降级不需要重启数据库。
3.1 改配置
3.2 reload (对已有连接和新建连接都会立即生效)。
3.3 cancel 等待信号(针对当前处于等待中的进程)。
4、降级后,什么情况下恢复为同步模式?(升级)
同样可以对(pg_stat_replication)状态进行监测,当sync standby处于streaming状态时,则可以转换为同步模式。
升级不需要重启数据库。
4.1 改配置
4.2 reload。立即生效 (对已有连接和新建连接都会立即生效)。
涉及技术点
1、事务提交参数
synchronous_commit
on, remote_apply, remote_write, local
2、同步配置参数
synchronous_standby_names
[FIRST] num_sync ( standby_name [, ...] )
ANY num_sync ( standby_name [, ...] )
standby_name [, ...]
ANY 3 (s1, s2, s3, s4)
FIRST 3 (s1, s2, s3, s4)
* 表示所有节点
3、活跃会话,查看事务提交时,等待事件状态
pg_stat_activity
等待事件
https://www.postgresql.org/docs/11/monitoring-stats.html#MONITORING-STATS-VIEWS
wait_event='SyncRep'
4、流状态,pg_stat_replication
sync_state='sync'
state
text
Current WAL sender state. Possible values are:
startup: This WAL sender is starting up.
catchup: This WAL sender's connected standby is catching up with the primary.
streaming: This WAL sender is streaming changes after its connected standby server has caught up with the primary.
backup: This WAL sender is sending a backup.
stopping: This WAL sender is stopping.
实践
环境
1、主
postgresql.conf
synchronous_commit = remote_write
wal_level = replica
max_wal_senders = 8
synchronous_standby_names = '*'
2、从
recovery.conf
restore_command = 'cp /data01/digoal/wal/%f %p'
primary_conninfo = 'host=localhost port=8001 user=postgres'
同步降级、升级 - 实践
关闭standby,模拟备库异常。看如何实现半同步。
模拟STANDBY恢复,看如何模拟升级为同步模式。
1、监测 pg_stat_activity,如果发现事务提交等待超过一定阈值(RTO保障),降级
select max(now()-query_start) from pg_stat_activity where wait_event='SyncRep';
2、查看以上结果等待时间(RTO保障)
当大于某个阈值时,开始降级。
注意NULL保护,NULL表示没有事务处于 SyncRep 等待状态。
3、降级步骤1,修改synchronous_commit参数。改成WAL本地持久化(异步流复制)。
alter system set synchronous_commit=local;
4、降级步骤2,生效参数,RELOAD
select pg_reload_conf();
5、降级步骤3,清空当前等待队列(处于SyncRep等待状态的进程在收到CANCEL信号后,从队列清空,并提示客户端,当前事务本地WAL已持久化,事务正常结束。)
select pg_cancel_backend(pid) from pg_stat_activity where wait_event='SyncRep';
6、收到清空信号的客户端返回正常(客户端可以看到事务正常提交)
postgres=# end;
WARNING: 01000: canceling wait for synchronous replication due to user request
DETAIL: The transaction has already committed locally, but might not have been replicated to the standby.
LOCATION: SyncRepWaitForLSN, syncrep.c:264
COMMIT
事务的redo信息已在本地WAL持久化,提交状态正常。
当前会话后续的请求会变成异步流复制模式(WAL本地持久化模式(synchronous_commit=local))。
如何升级?:
7、升级步骤1,监测standby状态,sync_state='sync'状态的standby进入streaming状态后,表示该standby与primary的wal已完全同步。
select * from pg_stat_replication where sync_state='sync' and state='streaming';
有结果返回,表示standby已经接收完primary的wal,可以进入同步模式。
8、升级步骤2,将事务提交模式改回同步模式( synchronous_commit=remote_write ,事务提交时,等sync standby接收到wal,并write。)
alter system set synchronous_commit=remote_write;
9、升级步骤3,生效参数,RELOAD (所有会话重置synchronous_commit=remote_write,包括已有连接,新建的连接)
select pg_reload_conf();
小结
1、在不修改PG内核的情况下,通过外部辅助监测和操纵(例如5秒监控间隔)),实现了两节点的半同步模式,在双节点或单节点正常的情况下,保证RPO=0,同时RTO可控(例如最长wait_event='SyncRep'等待时间超过10秒)。
2、内核修改建议,
降级:可以在等待队列中加HOOK,wait_event='SyncRep'等待超时后降级为异步。
升级:在wal_sender代码中加hook,监测到standby恢复后,改回同步模式。
参考
《PostgreSQL 一主多从(多副本,强同步)简明手册 - 配置、压测、监控、切换、防脑裂、修复、0丢失 - 珍藏级》
https://www.postgresql.org/docs/11/monitoring-stats.html#MONITORING-STATS-VIEWS
PostgreSQL 许愿链接
您的愿望将传达给PG kernel hacker、数据库厂商等, 帮助提高数据库产品质量和功能, 说不定下一个PG版本就有您提出的功能点. 针对非常好的提议,奖励限量版PG文化衫、纪念品、贴纸、PG热门书籍等,奖品丰富,快来许愿。开不开森.