雲計算

PostgreSQL 使用邏輯decode實現異步主從切換後,時間線分歧變化量補齊、修復

PostgreSQL 使用邏輯decode實現異步主從切換後,時間線分歧變化量補齊、修復

作者

digoal

日期

2019-01-29

標籤

PostgreSQL , pg_rewind , 時間線 , 變化量 , 業務補齊


背景

pg_rewind類似Oracle flashback,可以將一個數據庫回退到一個以前的狀態,例如用於:

1、PG物理流複製的從庫,當激活後,可以開啟讀寫,使用pg_rewind可以將從庫回退為只讀從庫的角色。而不需要重建整個從庫。

2、當異步主從發生角色切換後,主庫的wal目錄中可能還有沒完全同步到從庫的內容,因此老的主庫無法直接切換為新主庫的從庫。使用pg_rewind可以修復老的主庫,使之成為新主庫的只讀從庫。而不需要重建整個從庫。

如果沒有pg_rewind,遇到以上情況,需要完全重建從庫,如果庫佔用空間很大,重建非常耗時,也非常耗費上游數據庫的資源(讀)。

詳見:

《PostgreSQL pg_rewind,時間線修復,腦裂修復 - 從庫開啟讀寫後,回退為只讀從庫。異步主從發生角色切換後,主庫rewind為新主庫的從庫》

以上解決的是怎麼回退的問題,還有一個問題沒有解,在分歧點到當前狀態下,這些被回退掉的WAL,其中包含了哪些邏輯變化,這些信息怎麼補齊?

時間線分歧變化量補齊原理

1、開啟wal_level=logical

1.1、確保有足夠的slots

2、開啟DDL定義功能,參考:

《PostgreSQL 邏輯訂閱 - DDL 訂閱 實現方法》

3、在主庫,為每一個數據庫(或需要做時間線補齊的數據庫)創建一個logical SLOT

4、有更新、刪除操作的表,必須有主鍵

5、間歇性移動slot的位置到pg_stat_replication.sent_lsn的位置

6、如果從庫被激活,假設老主庫上還有未發送到從庫的WAL

7、從從庫獲取激活位置LSN

8、由於使用了SLOT,所以從庫激活位點LSN之後的WAL一定存在於老主庫WAL目錄中。

9、將老主庫的slot移動到激活位置LSN

10、從激活位置開始獲取logical變化量

11、業務層根據業務邏輯對這些變化量進行處理,補齊時間線分歧

示例

環境使用:

《PostgreSQL pg_rewind,時間線修復,腦裂修復 - 從庫開啟讀寫後,回退為只讀從庫。異步主從發生角色切換後,主庫rewind為新主庫的從庫》

主庫

port 4001  

從庫

port 4000  

1、開啟wal_level=logical

psql -p 4000  
  
postgres=# alter system set wal_level=logical;  
ALTER SYSTEM  
  
psql -p 4001  
  
postgres=# alter system set wal_level=logical;  
ALTER SYSTEM  

1.1、確保有足夠的slots

edb=# show max_replication_slots ;  
 max_replication_slots   
-----------------------  
 16  
(1 row)  

重啟數據庫。

2、開啟DDL定義功能,參考:

《PostgreSQL 邏輯訂閱 - DDL 訂閱 實現方法》

3、在主庫,為每一個數據庫(或需要做時間線補齊的數據庫)創建一個logical SLOT

postgres=# select pg_create_logical_replication_slot('fix_tl','test_decoding');  
 pg_create_logical_replication_slot   
------------------------------------  
 (fix_tl,B/73000140)  
(1 row)  
  
edb=# select pg_create_logical_replication_slot('fix_tl_edb','test_decoding');  
 pg_create_logical_replication_slot   
------------------------------------  
 (fix_tl_edb,B/73000140)  
(1 row)  

4、有更新、刪除操作的表,必須有主鍵

5、間歇性移動slot的位置到pg_stat_replication.sent_lsn的位置

連接到對應的庫操作  
  
postgres=# select pg_replication_slot_advance('fix_tl',sent_lsn) from pg_stat_replication ;  
 pg_replication_slot_advance   
-----------------------------  
 (fix_tl,B/73000140)  
(1 row)  
  
edb=# select pg_replication_slot_advance('fix_tl_edb',sent_lsn) from pg_stat_replication ;  
 pg_replication_slot_advance   
-----------------------------  
 (fix_tl,B/73000140)  
(1 row)  

6、如果從庫被激活,假設老主庫上還有未發送到從庫的WAL

pg_ctl promote -D /data04/ppas11/pg_root4000  

7、從從庫獲取激活位置LSN

cd /data04/ppas11/pg_root4000  
  
cat pg_wal/00000003.history   
  
1       8/48DE2318      no recovery target specified  
  
2       D/FD5FFFB8      no recovery target specified  

8、由於使用了SLOT,所以從庫激活位點LSN之後的WAL一定存在於老主庫WAL目錄中。

9、將老主庫的slot移動到激活位置LSN

psql -p 4001 postgres  
  
postgres=# select pg_replication_slot_advance('fix_tl','D/FD5FFFB8');  
  
psql -p 4001 edb  
  
edb=# select pg_replication_slot_advance('fix_tl_edb','D/FD5FFFB8');  

10、從激活位置開始獲取logical變化量

edb=# select * from pg_logical_slot_get_changes('fix_tl_edb',NULL,10,'include-xids', '0');  
 lsn | xid | data   
-----+-----+------  
(0 rows)  
由於EDB庫沒有變化,所以返回0條記錄  
postgres=# select * from pg_logical_slot_get_changes('fix_tl',NULL,10,'include-xids', '0');  
    lsn     |   xid    |                                                                                                      data                                                                                                        
------------+----------+----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------  
 D/FD5FEC60 | 68900576 | BEGIN  
 D/FD5FEC60 | 68900576 | table public.pgbench_accounts: UPDATE: aid[integer]:44681547 bid[integer]:447 abalance[integer]:-4591 filler[character]:'                                                                                    '  
 D/FD5FF3A8 | 68900576 | table public.pgbench_tellers: UPDATE: tid[integer]:5091 bid[integer]:510 tbalance[integer]:-160944 filler[character]:null  
 D/FD5FF9A8 | 68900576 | table public.pgbench_branches: UPDATE: bid[integer]:740 bbalance[integer]:-261044 filler[character]:null  
 D/FD5FFEF8 | 68900576 | table public.pgbench_history: INSERT: tid[integer]:5091 bid[integer]:740 aid[integer]:44681547 delta[integer]:-4591 mtime[timestamp without time zone]:'29-JAN-19 09:48:14.39739' filler[character]:null  
 D/FD6001E8 | 68900576 | COMMIT  
 D/FD5FE790 | 68900574 | BEGIN  
 D/FD5FE790 | 68900574 | table public.pgbench_accounts: UPDATE: aid[integer]:60858810 bid[integer]:609 abalance[integer]:3473 filler[character]:'                                                                                    '  
 D/FD5FF1C8 | 68900574 | table public.pgbench_tellers: UPDATE: tid[integer]:8829 bid[integer]:883 tbalance[integer]:60244 filler[character]:null  
 D/FD5FF810 | 68900574 | table public.pgbench_branches: UPDATE: bid[integer]:33 bbalance[integer]:86295 filler[character]:null  
 D/FD5FFD80 | 68900574 | table public.pgbench_history: INSERT: tid[integer]:8829 bid[integer]:33 aid[integer]:60858810 delta[integer]:3473 mtime[timestamp without time zone]:'29-JAN-19 09:48:14.397383' filler[character]:null  
 D/FD600218 | 68900574 | COMMIT  
(12 rows)  
  
postgres=# select * from pg_logical_slot_get_changes('fix_tl',NULL,10,'include-xids', '0');  
    lsn     |   xid    |                                                                                                      data                                                                                                        
------------+----------+----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------  
 D/FD5FEED0 | 68900578 | BEGIN  
 D/FD5FEED0 | 68900578 | table public.pgbench_accounts: UPDATE: aid[integer]:15334791 bid[integer]:154 abalance[integer]:-2741 filler[character]:'                                                                                    '  
 D/FD5FF518 | 68900578 | table public.pgbench_tellers: UPDATE: tid[integer]:2402 bid[integer]:241 tbalance[integer]:191936 filler[character]:null  
 D/FD5FFB88 | 68900578 | table public.pgbench_branches: UPDATE: bid[integer]:345 bbalance[integer]:-693783 filler[character]:null  
 D/FD5FFFB8 | 68900578 | table public.pgbench_history: INSERT: tid[integer]:2402 bid[integer]:345 aid[integer]:15334791 delta[integer]:-2741 mtime[timestamp without time zone]:'29-JAN-19 09:48:14.397396' filler[character]:null  
 D/FD600248 | 68900578 | COMMIT  
 D/FD5FF438 | 68900579 | BEGIN  
 D/FD5FF438 | 68900579 | table public.pgbench_accounts: UPDATE: aid[integer]:54259132 bid[integer]:543 abalance[integer]:3952 filler[character]:'                                                                                    '  
 D/FD5FFEA8 | 68900579 | table public.pgbench_tellers: UPDATE: tid[integer]:9591 bid[integer]:960 tbalance[integer]:-498586 filler[character]:null  
 D/FD600298 | 68900579 | table public.pgbench_branches: UPDATE: bid[integer]:147 bbalance[integer]:459542 filler[character]:null  
 D/FD600560 | 68900579 | table public.pgbench_history: INSERT: tid[integer]:9591 bid[integer]:147 aid[integer]:54259132 delta[integer]:3952 mtime[timestamp without time zone]:'29-JAN-19 09:48:14.397464' filler[character]:null  
 D/FD600938 | 68900579 | COMMIT  
(12 rows)  
... ...  
  
直到沒有記錄返回,說明已獲取到所有變化量  

直到沒有記錄返回,說明已獲取到所有變化量

10.1、查看SLOT狀態,當前WAL位置信息

psql -p 4001   
  
postgres=# select * from pg_get_replication_slots();  
 slot_name  |    plugin     | slot_type | datoid | temporary | active | active_pid | xmin | catalog_xmin | restart_lsn | confirmed_flush_lsn   
------------+---------------+-----------+--------+-----------+--------+------------+------+--------------+-------------+---------------------  
 fix_tl     | test_decoding | logical   |  15844 | f         | f      |            |      |     67005646 | D/D7959218  | D/FD600218  
 fix_tl_edb | test_decoding | logical   |  15845 | f         | f      |            |      |     72528996 | E/71C92B00  | E/71C92B38  
(2 rows)  
  
當前WAL位置  
  
postgres=# select pg_current_wal_lsn();  
 pg_current_wal_lsn   
--------------------  
 E/71C92B38  
(1 row)  

11、業務層根據業務邏輯對這些變化量進行處理,補齊時間線分歧

小結

主庫開啟邏輯SLOT,並根據從庫的接收LSN位置,使用pg_replication_slot_advance移動主庫的slot位點到從庫的接收LSN位置。

當從庫激活,老主庫還有未同步到從庫的WAL時,可以通過邏輯decode的方法,獲取到未同步的邏輯變化量。

業務層根據業務邏輯,補齊這些變化量到新的主庫。

注意:

1、開啟logical wal_level,會給數據庫增加較多的WAL日誌,請酌情開啟。

2、開啟SLOT後,由於數據庫會保證沒有被訂閱的WAL保留在pg_wal目錄中,那麼如果SLOT沒有及時移動,則可能導致主庫的pg_wal目錄暴增。

參考

https://www.postgresql.org/docs/11/test-decoding.html

https://www.postgresql.org/docs/11/functions-admin.html#FUNCTIONS-REPLICATION

《PostgreSQL 邏輯訂閱 - DDL 訂閱 實現方法》

《PostgreSQL pg_rewind,時間線修復,腦裂修復 - 從庫開啟讀寫後,回退為只讀從庫。異步主從發生角色切換後,主庫rewind為新主庫的從庫》

免費領取阿里雲RDS PostgreSQL實例、ECS虛擬機

大量阿里雲PG解決方案: 任意維度實時圈人; 時序數據實時處理; 時間、空間、業務 多維數據實時透視; 獨立事件相關性分析; 海量關係實時圖式搜索; 社交業務案例; 流式數據實時處理案例; 物聯網; 全文檢索; 模糊、正則查詢案例; 圖像識別; 向量相似檢索; 數據清洗、採樣、脫敏、批處理、合併; GIS 地理信息空間數據應用; 金融業務; 異步消息應用案例; 海量數據 冷熱分離; 倒排索引案例; 海量數據OLAP處理應用;

德哥的 / digoal's PostgreSQL文章入口 - 努力做成PG資源最豐富的個人blog

德哥的微信 / digoal's wechat

Leave a Reply

Your email address will not be published. Required fields are marked *