postgres主备切换之文件触发方式详解
本文测试参考PostgresSQL实战一书。
本文档测试环境:
主库IP:192.168.40.130主机名:postgres端口:5442
备库IP:192.168.40.131主机名:postgreshot端口:5442
PostgreSQL9.0版本流复制主备切换只能通过创建触发文件方式进行,这一小节将介绍这种主备切换方式,测试环境为一主一备异步流复制环境,postgres上的数据库为主库,postgreshot上的数据库为备库,文件触发方式的手工主备切换主要步骤如下:
1)配置备库recovery.conf文件trigger_file参数,设置激活备库的触发文件路径和名称。
2)关闭主库,建议使用-mfast模式关闭。
3)在备库上创建触发文件激活备库,如果recovery.conf变成recovery.done表示备库已经切换成主库。
4)这时需要将老的主库切换成备库,在老的主库的$PGDATA目录下创建recovery.conf文件(如果此目录下不存在recovery.conf文件,可以根据$PGHOME/share/recovery.conf.sample模板文件复制一个,如果此目录下存在recovery.done文件,需将recovery.done文件重命名为recovery.conf),配置和老的从库一样,只是primary_conninfo参数中的IP换成对端IP。
5)启动老的主库,这时观察主、备进程是否正常,如果正常表示主备切换成功。
1、首先在备库上配置recovery.conf,如下所示:
[postgres@postgreshotpg11]$catrecovery.conf|grep-v'^#' recovery_target_timeline='latest' standby_mode=on primary_conninfo='host=192.168.40.130port=5442user=replicaapplication_name=pg1'#e.g.'host=localhostport=5432' trigger_file='/home/postgres/pg11/trigger' [postgres@postgreshotpg11]$
trigger_file可以配置成普通文件或隐藏文件,调整以上参数后需重启备库使配置参数生效。
2、关闭主库,如下所示:
[postgres@postgrespg11]$pg_ctlstop-mfast waitingforservertoshutdown....done serverstopped [postgres@postgrespg11]$
3、在备库上创建触发文件激活备库,如下所示:
[postgres@postgreshotpg11]$llrecovery.conf -rwx------1postgrespostgres5.9KMar2618:47recovery.conf [postgres@postgreshotpg11]$ [postgres@postgreshotpg11]$touch/home/postgres/pg11/trigger [postgres@postgreshotpg11]$llrecovery* -rwx------1postgrespostgres5.9KMar2618:47recovery.done [postgres@postgreshotpg11]$
触发器文件名称和路径需和recovery.conf配置文件trigger_file保持一致,再次查看recovery文件时,发现后辍由原来的.conf变成了.done
查看备库数据库日志,如下所示:
2019-03-2623:30:19.399EDT[93162]LOG:replicationterminatedbyprimaryserver 2019-03-2623:30:19.399EDT[93162]DETAIL:EndofWALreachedontimeline3at0/50003D0. 2019-03-2623:30:19.399EDT[93162]FATAL:couldnotsendend-of-streamingmessagetoprimary:noCOPYinprogress 2019-03-2623:30:19.399EDT[93158]LOG:invalidrecordlengthat0/50003D0:wanted24,got0 2019-03-2623:30:19.405EDT[125172]FATAL:couldnotconnecttotheprimaryserver:serverclosedtheconnectionunexpectedly Thisprobablymeanstheserverterminatedabnormally beforeorwhileprocessingtherequest. 2019-03-2623:30:24.410EDT[125179]FATAL:couldnotconnecttotheprimaryserver:couldnotconnecttoserver:Connectionrefused Istheserverrunningonhost"192.168.40.130"andaccepting TCP/IPconnectionsonport5442? 2019-03-2623:31:49.505EDT[93158]LOG:triggerfilefound:/home/postgres/pg11/trigger 2019-03-2623:31:49.506EDT[93158]LOG:redodoneat0/5000360 2019-03-2623:31:49.506EDT[93158]LOG:lastcompletedtransactionwasatlogtime2019-03-2619:03:11.202845-04 2019-03-2623:31:49.516EDT[93158]LOG:selectednewtimelineID:4 2019-03-2623:31:50.063EDT[93158]LOG:archiverecoverycomplete 2019-03-2623:31:50.083EDT[93157]LOG:databasesystemisreadytoacceptconnections
根据备库以上信息,由于关闭了主库,首先日志显示连接不上主库,接着显示发现了触发文件,之后显示恢复成功,数据库切换成读写模式。
这时根据pg_controldata输出进行验证,如下所示:
[postgres@postgreshot~]$pg_controldata|grepcluster Databaseclusterstate:inproduction [postgres@postgreshot~]$
以上显示数据库角色已经是主库角色,在postgreshot上创建一张名为test_alived的表并插入数据,如下所示:
postgres=#CREATETABLEtest_alived2(idint4); CREATETABLE postgres=#INSERTINTOtest_alived2VALUES(1); INSERT01 postgres=#
4、准备将老的主库切换成备库角色,在老的主库上配置recovery.conf,如下所示:
[postgres@postgrespg11]$catrecovery.conf|grep-v'^#' recovery_target_timeline='latest' standby_mode=on primary_conninfo='host=192.168.40.131port=5442user=replicaapplication_name=pg2'#e.g.'host=localhostport=5432' trigger_file='/home/postgres/pg11/trigger' [postgres@postgrespg11]$
以上配置和postgreshot上的recovery.done配置文件基本一致,只是primary_conninfo参数的host选项配置成对端主机IP。
之后在postgres主机用户家目录创建~/.pgpass文件,如下所示:
[postgres@pghost1~]$touch~/.pgpass [postgres@pghost1~]$chmod600~/.pgpass
并在~/.pgpass文件中插入以下内容:
[postgres@postgres~]$cat.pgpass 192.168.40.130:5442:replication:replica:replica 192.168.40.131:5442:replication:replica:replica [postgres@postgres~]
之后启动postgres上的数据库,如下所示:
[postgres@postgres~]$pg_ctlstart waitingforservertostart....2019-03-2623:38:50.424EDT[55380]LOG:listeningonIPv4address"0.0.0.0",port5442 2019-03-2623:38:50.424EDT[55380]LOG:listeningonIPv6address"::",port5442 2019-03-2623:38:50.443EDT[55380]LOG:listeningonUnixsocket"/tmp/.s.PGSQL.5442" 2019-03-2623:38:50.465EDT[55381]LOG:databasesystemwasshutdowninrecoveryat2019-03-2623:38:20EDT 2019-03-2623:38:50.465EDT[55381]LOG:enteringstandbymode 2019-03-2623:38:50.483EDT[55381]LOG:consistentrecoverystatereachedat0/50003D0 2019-03-2623:38:50.483EDT[55381]LOG:invalidrecordlengthat0/50003D0:wanted24,got0 2019-03-2623:38:50.483EDT[55380]LOG:databasesystemisreadytoacceptreadonlyconnections done serverstarted [postgres@postgres~]$2019-03-2623:38:50.565EDT[55385]LOG:fetchingtimelinehistoryfilefortimeline4fromprimaryserver 2019-03-2623:38:50.588EDT[55385]LOG:startedstreamingWALfromprimaryat0/5000000ontimeline3 2019-03-2623:38:50.589EDT[55385]LOG:replicationterminatedbyprimaryserver 2019-03-2623:38:50.589EDT[55385]DETAIL:EndofWALreachedontimeline3at0/50003D0. 2019-03-2623:38:50.592EDT[55381]LOG:newtargettimelineis4 2019-03-2623:38:50.594EDT[55385]LOG:restartedWALstreamingat0/5000000ontimeline4 2019-03-2623:38:50.717EDT[55381]LOG:redostartsat0/50003D0 [postgres@postgres~]$pg_controldata|grepcluster Databaseclusterstate:inarchiverecovery [postgres@postgres~]$ postgres=#select*fromtest_alived2; id ---- 1 (1row) postgres=#
同时,postgres上已经有了WAL接收进程,postgreshot上有了WAL发送进程,说明老的主库已经成功切换成备库,以上是主备切换的所有步骤。
为什么在步骤2中需要干净地关闭主库?数据库关闭时首先做一次checkpoint,完成之后通知WAL发送进程要关闭了,WAL发送进程会将截止此次checkpoint的WAL日志流发送给备库的WAL接收进程,备节点接收到主库最后发送来的WAL日志流后应用WAL,从而达到了和主库一致的状态。
另一个需要注意的问题是假如主库主机异常宕机了,如果激活备库,备库的数据完全和主库一致吗?此环境为一主一备异步流复制环境,备库和主库是异步同步方式,存在延时,这时主库上已提交事务的WAL有可能还没来得及发送给备库,主库主机就已经宕机了,因此异步流复制备库可能存在事务丢失的风险。
主备切换之pg_ctlpromote方式
上面介绍了以文件触发方式进行主备切换,PostgreSQL9.1版本开始支持pg_ctlpromote触发方式,相比文件触发方式操作更方便,promote命令语法如下:
pg_ctlpromote[-Ddatadir]
-D是指数据目录,如果不指定会使用环境变量$PGDATA设置的值。promote命令发出后,运行中的备库将停止恢复模式并切换成读写模式的主库。
pg_ctlpromote主备切换步骤和文件触发方式大体相同,只是步骤1中不需要配置recovery.conf配置文件中的trigger_file参数,并且步骤3中换成以pg_ctlpromote方式进行主备切换,如下:
1)关闭主库,建议使用-mfast模式关闭。
2)在备库上执行pg_ctlpromote命令激活备库,如果recovery.conf变成recovery.done表示备库已切换成为主库。
3)这时需要将老的主库切换成备库,在老的主库的$PGDATA目录下创建recovery.conf文件(如果此目录下不存在recovery.conf文件,可以根据$PGHOME/share/recovery.conf.sample模板文件复制一个,如果此目录下存在recovery.done文件,需将recovery.done文件重命名为recovery.conf),配置和老的从库一样,只是primary_conninfo参数中的IP换成对端IP。
4)启动老的主库,这时观察主、备进程是否正常,如果正常表示主备切换成功。以上是pg_ctlpromote主备切换的主要步骤,这一小节不进行演示了,下一小节介绍pg_rewind工具时会给出使用pg_ctlpromote进行主备切换的示例
pg_rewind
pg_rewind是流复制维护时一个非常好的数据同步工具,在上一节介绍流复制主备切换内容中讲到了主要有五个步骤进行主备切换,其中步骤2是在激活备库前先关闭主库,如果不做步骤2会出现什么样的情况?下面我们举例进行演示,测试环境为一主一备异步流复制环境,postgres上的数据库为主库,postgreshot上的数据库为备库。
主备切换
--备节点recovery.conf配置:postgreshot上操作
备库recovery.conf配置如下所示:
[postgres@postgreshotpg11]$catrecovery.conf|grep-v'^#' recovery_target_timeline='latest' standby_mode=on primary_conninfo='host=192.168.40.130port=5442user=replicaapplication_name=pg1'#e.g.'host=localhostport=5432' trigger_file='/home/postgres/pg11/trigger' [postgres@postgreshotpg11]$
--激活备节点:postgreshot上操作
检查流复制状态,确保正常后在备库主机上执行以下命令激活备库,如下所示
[postgres@postgreshotpg11]$pg_ctlpromote-D$PGDATA waitingforservertopromote....done serverpromoted [postgres@postgreshotpg11]$ [postgres@postgreshotpg11]$
查看备库数据库日志,能够看到数据库正常打开接收外部连接的信息,这说明激活成功,检查postgreshot上的数据库角色,如下所示:
[postgres@postgreshotpg11]$pg_controldata|grepcluster Databaseclusterstate:inproduction [postgres@postgreshotpg11]$
从pg_controldata输出也可以看到postgreshot上的数据库已成为主库,说明postgreshot上的数据库已经切换成主库,这时老的主库(postgres上的数据库)依然还在运行中,我们计划将postgres上的角色转换成备库,先查看postgres上的数据库角色,如下所示
[postgres@postgrespg11]$pg_controldata|grepcluster Databaseclusterstate:inproduction [postgres@postgrespg11]$
--备节点激活后,创建一张测试表并插入数据
postgres=#createtabletest_1(idint4); CREATETABLE postgres=#insertintotest_1(id)selectnfromgenerate_series(1,10)n; INSERT010 postgres=#
--停原来主节点:postgres上操作
[postgres@postgrespg11]$pg_controldata|grepcluster Databaseclusterstate:inproduction [postgres@postgrespg11]$ [postgres@postgrespg11]$pg_ctlstop-mfast-D$PGDATA 2019-03-2701:10:46.714EDT[64858]LOG:receivedfastshutdownrequest waitingforservertoshutdown....2019-03-2701:10:46.716EDT[64858]LOG:abortinganyactivetransactions 2019-03-2701:10:46.717EDT[64858]LOG:backgroundworker"logicalreplicationlauncher"(PID64865)exitedwithexitcode1 2019-03-2701:10:46.718EDT[64860]LOG:shuttingdown 2019-03-2701:10:46.731EDT[64858]LOG:databasesystemisshutdown done serverstopped [postgres@postgrespg11]$
--pg_rewind:postgres上操作
[postgres@postgreshotpg11]$pg_rewind--target-pgdata$PGDATA--source-server='host=192.168.40.131port=5442user=replicapassword=replica' targetserverneedstouseeitherdatachecksumsor"=on" Failure,exiting [postgres@postgreshotpg11]$
备注:数据库在initdb时需要开启checksums或者设置"wal_log_hints=on",接着设置主,备节点的wal_log_hints参数并重启数据库。
[postgres@postgrespg11]$pg_rewind--target-pgdata$PGDATA--source-server='host=192.168.40.131port=5442user=replicapassword=replica' serversdivergedatWALlocation0/70001E8ontimeline5 rewindingfromlastcommoncheckpointat0/6000098ontimeline5 Done! [postgres@postgrespg11]$ [postgres@postgrespg11]$
备注:pg_rewind成功。
--调整recovery.conf文件:postgres操作
[postgres@postgrespg11]$mvrecovery.donerecovery.conf [postgres@postgrespg11]$ [postgres@postgrespg11]$catrecovery.conf|grep-v'^#' recovery_target_timeline='latest' standby_mode=on primary_conninfo='host=192.168.40.131port=5442user=replicaapplication_name=pg2'#e.g.'host=localhostport=5432' trigger_file='/home/postgres/pg11/trigger' [postgres@postgrespg11]$
--启动原主库,postgres上操作
[postgres@postgrespg11]$pg_ctlstart-D$PGDATA waitingforservertostart....2019-03-2701:14:48.028EDT[66323]LOG:listeningonIPv4address"0.0.0.0",port5442 2019-03-2701:14:48.028EDT[66323]LOG:listeningonIPv6address"::",port5442 2019-03-2701:14:48.031EDT[66323]LOG:listeningonUnixsocket"/tmp/.s.PGSQL.5442" 2019-03-2701:14:48.045EDT[66324]LOG:databasesystemwasinterruptedwhileinrecoveryatlogtime2019-03-2701:08:08EDT 2019-03-2701:14:48.045EDT[66324]HINT:Ifthishasoccurredmorethanoncesomedatamightbecorruptedandyoumightneedtochooseanearlierrecoverytarget. 2019-03-2701:14:48.084EDT[66324]LOG:enteringstandbymode 2019-03-2701:14:48.089EDT[66324]LOG:redostartsat0/6000060 2019-03-2701:14:48.091EDT[66324]LOG:invalidrecordlengthat0/7024C98:wanted24,got0 2019-03-2701:14:48.096EDT[66331]LOG:startedstreamingWALfromprimaryat0/7000000ontimeline6 2019-03-2701:14:48.109EDT[66324]LOG:consistentrecoverystatereachedat0/7024CD0 2019-03-2701:14:48.110EDT[66323]LOG:databasesystemisreadytoacceptreadonlyconnections done serverstarted [postgres@postgrespg11]$ [postgres@postgrespg11]$pg_controldata|grepcluster Databaseclusterstate:inarchiverecovery [postgres@postgrespg11]$
--数据验证,postgres上操作
[postgres@postgrespg11]$p psql(11.1) Type"help"forhelp. postgres=#selectcount(*)fromtest_1; count ------- 10 (1row) postgres=#
备注:pg_rewind成功,原主库现在是以备库角色启动,而且数据表test_1也同步过来了。
pg_rewind原理
Thebasicideaistocopyeverythingfromthenewclustertotheoldcluster,exceptfortheblocksthatweknowtobethesame.
1)ScantheWALlogoftheoldcluster,startingfromthelastcheckpointbeforethepointwherethenewcluster'stimelinehistoryforkedofffromtheoldcluster.ForeachWALrecord,makeanoteofthedatablocksthatweretouched.Thisyieldsalistofallthedatablocksthatwerechangedintheoldcluster,afterthenewclusterforkedoff.
2)Copyallthosechangedblocksfromthenewclustertotheoldcluster.
3)Copyallotherfileslikeclog,conffilesetc.fromthenewclustertooldcluster.Everythingexcepttherelationfiles.
4)ApplytheWALfromthenewcluster,startingfromthecheckpointcreatedatfailover.(Strictlyspeaking,pg_rewinddoesn'tapplytheWAL,itjustcreatesabackuplabelfileindicatingthatwhenPostgreSQLisstarted,itwillstartreplayfromthatcheckpointandapplyalltherequiredWAL.)
补充:postgres主备搭建时踩坑点
搭建pg主备流复制时的踩坑集锦
1:socket路径问题报错如下
你好!这是你第一次使用**Markdown编辑器**所展示的欢迎页。如果你想学习如何使用Markdown编辑器,仔细阅读这篇文章,了解一下Markdown的基本语法知识。解决方法:修改postgres.conf中unix_socket_permissions=‘*'路径修改为上述报错中的路径重启即可
2:搭建主备时备库的data目录一定一定一定要使用主库基础备份出来的数据。可采用pg_basebackup的方式,也可以采用tar包打包解包的方式进行基础备份
如果备库不小心已经初始化过请删除data目录下的*并使用主库的基础备份重新启动
3:备库启动时报错FATAL:nopg_hba.confentryforreplicationconnectionfromhost“172.20.0.16”,user“repl”之类的问题
例如master:IP:*.1standby:IP*.2主备账号repl
那么在pg_hba.cnf中单单指明hostreplicationrepl*.2md5是不行的
还需在此条记录前面添加hostallall*.2md5
首先要能访问主库才会资格使用repl账号进行同步的步骤
以上为个人经验,希望能给大家一个参考,也希望大家多多支持毛票票。如有错误或未考虑完全的地方,望不吝赐教。