PostgreSQL 使用逻辑decode实现异步主从切换后,时间线分歧变化量补齐、修复

背景

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为新主库的从库》

PostgreSQL 许愿链接

您的愿望将传达给PG kernel hacker、数据库厂商等, 帮助提高数据库产品质量和功能, 说不定下一个PG版本就有您提出的功能点. 针对非常好的提议,奖励限量版PG文化衫、纪念品、贴纸、PG热门书籍等,奖品丰富,快来许愿。开不开森.

9.9元购买3个月阿里云RDS PostgreSQL实例

PostgreSQL 解决方案集合

上一篇:perl对字符串的处理


下一篇:看了就想结婚