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

本文涉及的产品
云原生数据库 PolarDB MySQL 版,Serverless 5000PCU 100GB
云原生数据库 PolarDB 分布式版,标准版 2核8GB
云数据库 RDS MySQL Serverless,0.5-2RCU 50GB
简介: 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 解决方案集合

相关实践学习
使用PolarDB和ECS搭建门户网站
本场景主要介绍基于PolarDB和ECS实现搭建门户网站。
阿里云数据库产品家族及特性
阿里云智能数据库产品团队一直致力于不断健全产品体系,提升产品性能,打磨产品功能,从而帮助客户实现更加极致的弹性能力、具备更强的扩展能力、并利用云设施进一步降低企业成本。以云原生+分布式为核心技术抓手,打造以自研的在线事务型(OLTP)数据库Polar DB和在线分析型(OLAP)数据库Analytic DB为代表的新一代企业级云原生数据库产品体系, 结合NoSQL数据库、数据库生态工具、云原生智能化数据库管控平台,为阿里巴巴经济体以及各个行业的企业客户和开发者提供从公共云到混合云再到私有云的完整解决方案,提供基于云基础设施进行数据从处理、到存储、再到计算与分析的一体化解决方案。本节课带你了解阿里云数据库产品家族及特性。
相关文章
|
Prometheus 监控 Cloud Native
手把手教你Prometheus + Granafa实现mysql 性能监测部署
数据库性能监控可以说是十分重要,能否自行搭建环境实现像阿里云或是腾讯云那样直观的展示不同维度数据的功能?答案是肯定的。下面详细说明一下安装部署过程以及过程中出现的问题,希望对你有所帮助!
手把手教你Prometheus + Granafa实现mysql 性能监测部署
|
2月前
|
SQL 关系型数据库 分布式数据库
PolarDB for PostgreSQL逻辑复制问题之逻辑复制冲突如何解决
PolarDB for PostgreSQL是基于PostgreSQL开发的一款云原生关系型数据库服务,它提供了高性能、高可用性和弹性扩展的特性;本合集将围绕PolarDB(pg)的部署、管理和优化提供指导,以及常见问题的排查和解决办法。
|
4月前
|
存储 人工智能 关系型数据库
postgresql从入门到精通教程 - 第36讲:postgresql逻辑备份
postgresql从入门到精通教程 - 第36讲:postgresql逻辑备份
145 1
|
9月前
|
监控 关系型数据库 API
PostgreSQL 13、14中逻辑复制/解码改进
PostgreSQL 13、14中逻辑复制/解码改进
129 0
|
9月前
|
存储 关系型数据库 分布式数据库
PostgreSQL 14中两阶段提交的逻辑解码
PostgreSQL 14中两阶段提交的逻辑解码
122 0
|
9月前
|
SQL 机器学习/深度学习 存储
PostgreSQL逻辑备份pg_dump使用及其原理解析
PostgreSQL逻辑备份pg_dump使用及其原理解析
176 0
|
SQL 关系型数据库 MySQL
MySql字符串拆分实现split功能(字段分割转列、转行)
MySql字符串拆分实现split功能(字段分割转列、转行)
MySql字符串拆分实现split功能(字段分割转列、转行)
|
SQL 缓存 关系型数据库
MySQL日志(undo log 和 redo log 实现事务的原子性/持久性/一致性)
MySQL日志(undo log 和 redo log 实现事务的原子性/持久性/一致性)
MySQL日志(undo log 和 redo log 实现事务的原子性/持久性/一致性)
|
SQL 关系型数据库 MySQL
C语言连接并实现对MySQL的增删改查
C语言访问数据库并不如Java、Python那般容易。本文介绍C语言连接并实现对MySQL的增删改查的方法。
429 0
|
canal 关系型数据库 MySQL
基于 Docker 结合 Canal 实现 MySQL 实时增量数据传输
基于 Docker 结合 Canal 实现 MySQL 实时增量数据传输
基于 Docker 结合 Canal 实现 MySQL 实时增量数据传输

相关产品

  • 云原生数据库 PolarDB