我們知道postgresql的主從切換有點麻煩,或者說操作步驟要求很嚴格。可能我們經常遇到這種情況,在沒有將主庫殺死的情況下將備庫提升爲主,這時主備庫可能由於某種原因都在提供寫入操作,這時發生腦裂,如果不考慮數據丟失因素,這時我們可能想將原來的主庫以備庫的模式重新加入集羣,但是主備庫此時的時間線已經偏離了,這時就需要我們的pg_rewind工具了。
pg_rewind相比pg_basebackup和rsync這樣的工具來說,優勢是它不需要從源目錄拷貝所有的數據文件,而是會對比時間線發生偏離的點,只拷貝變化過的文件,這樣對於數據量很大的情況下速度更快。
備庫上運行pg_rewind會使得數據庫進入恢復狀態,備庫會從主庫讀取必要的wal文件,如果源庫上因爲跑了很長一段時間造成wal丟失,則可以手工從歸檔目錄進行拷貝。
下面的實驗簡單演示一下pg_rewind的使用:
環境:192.168.1.1(主),192.168.1.2(從)
前提:配置好主從同步,開啓wal_log_hints和full_page_writes參數,流複製的配置以及原理等相關詳細知識請參考我之前的文章,這裏不再贅述。
主庫查看同步狀態:
postgres=# select * from pg_stat_replication;
-[ RECORD 1 ]----+------------------------------
pid | 10984
usesysid | 16384
usename | replica
application_name | s1
client_addr | 192.168.1.2
client_hostname |
client_port | 40578
backend_start | 2019-09-11 13:57:53.525226+08
backend_xmin |
state | streaming
sent_lsn | 0/4002180
write_lsn | 0/4002180
flush_lsn | 0/4002180
replay_lsn | 0/4002180
write_lag |
flush_lag |
replay_lag |
sync_priority | 1
sync_state | sync
備庫執行升主操作:
[postgres@DB2 ~]$ pg_ctl promote -D /pgdata
waiting for server to promote.... done
server promoted
查看狀態發現主備此時都是生產狀態,兩個庫都可以接受寫入,其實就是腦裂的狀態:
[postgres@DB1 ~]$ pg_controldata -D /pgdata |grep cluster
Database cluster state: in production
[postgres@DB2 ~]$ pg_controldata /pgdata/ |grep cluster
Database cluster state: in production
修改老主庫的synchronous_standby_name參數,這裏修改的意義是如果不進行修改那麼主庫寫的東西還會向原來的備庫同步,而此時已經無法同步,就會hang在那,所以我們要取消原來的同步關係。
postgres=# alter system set synchronous_standby_names='';
ALTER SYSTEM
postgres=# \q
[postgres@DB1 pg_wal]$ pg_ctl reload -D /pgdata/
server signaled
這時在新主庫執行寫入操作:
postgres=# insert into t values(2);
INSERT 0 1
postgres=# select * from t;
id
——
2
(1 row)
原主庫也執行寫入操作,模擬時間線偏離:
postgres=# insert into t values(1);
INSERT 0 1
postgres=# select * from t;
id
——
1
(1 row)
接下來開始使用pg_rewind進行老主庫重搭,變爲備庫角色。
停止老主庫,一定要乾淨的關閉:
[postgres@DB1 ~]$ pg_ctl stop -D /pgdata/
waiting for server to shut down..... done
server stopped
執行pg_rewind,注意新主庫裏面要配置pg_hba.conf選項。
[postgres@DB1 pg_wal]$ pg_rewind -D '/pgdata' --source-server='host=192.168.1.2 user=postgres dbname=postgres connect_timeout=2' -P --dry-run
connected to server
servers diverged at WAL location 0/10017448 on timeline 1
could not open file "/pgdata/pg_wal/000000010000000000000010": 沒有那個文件或目錄
could not find previous WAL record at 0/10017448
Failure, exiting
發現報錯,提示xlog目錄下沒有000000010000000000000010這個日誌文件,因爲我們設置了歸檔,而且wal_keep_segments沒有設置,所以在數據庫關閉時,wal日誌歸檔到了歸檔目錄,此時我們將歸檔目錄的該日誌拷貝過來,再次執行:
[postgres@DB1 pg_wal]$ cp /pgarch/000000010000000000000010 /pgdata/pg_wal/
[postgres@DB1 pg_wal]$ pg_rewind -D '/pgdata' --source-server='host=192.168.1.2 user=postgres dbname=postgres connect_timeout=2' -P --dry-run
[postgres@DB1 pg_wal]$ pg_rewind -D '/pgdata' --source-server='host=192.168.1.2 user=postgres dbname=postgres connect_timeout=2' -P
connected to server
servers diverged at WAL location 0/10017448 on timeline 1
rewinding from last common checkpoint at 0/F000098 on timeline 1
reading source file list
reading target file list
reading WAL in target
need to copy 52 MB (total source directory size is 70 MB)
53302/53302 kB (100%) copied
creating backup label and updating control file
syncing target data directory
Done!
原主庫配置recovery.conf
vi recovery.conf
standby_mode = 'on'
primary_conninfo = 'host=192.168.1.2 port=5432 user=replica password=replica application_name=old_master'
recovery_target_timeline = 'latest'
啓動原主庫,並查看數據,發現原主庫插入的1已經沒有,在新主庫上插入的2已經同步過來,新的主備關係也正常了。
[postgres@DB1 pgdata]$ pg_ctl start -D /pgdata/ -l logfile
waiting for server to start.... done
server started
postgres=# select * from t;
id
——
2
(1 row)