关于PostgreSQL逻辑订阅中的复制状态

本文涉及的产品
云原生数据库 PolarDB MySQL 版,Serverless 5000PCU 100GB
简介: 关于PostgreSQL逻辑订阅中的复制状态

开启逻辑订阅后,我们要知道复制的状态。这可以通过PG中的几个系统表或视图获取。

订阅端

pg_subscription_rel

通过pg_subscription_rel可以知道每张表的同步状态

postgres=# select * from pg_subscription_rel;
 srsubid | srrelid | srsubstate | srsublsn  
---------+---------+------------+-----------
   18465 |   18446 | r          | 0/453EF50
   18465 |   18453 | r          | 0/453EF88
   18465 |   18459 | r          | 0/453EFC0
(3 rows)
  • srsubstate
    状态码: i = 初始化, d = 正在复制数据, s = 已同步, r = 准备好 (普通复制)
  • srsublsn
    s和r状态时源端的结束LSN。

初始时该表处于i状态,而后PG从发布端copy基表,此时该表处于d状态,基表拷贝完成后记录LSN位置到srsublsn。
之后进入s状态最后再进入r状态,并通过pgoutput逻辑解码从发布端拉取并应用增量数据。

s状态和r状态的区别是什么?
初始拷贝完成后,每个表的sync worker还需要从发布端拉取增量,直到增量部分追到大于等于apply worker的同步位置。
当追上apply worker的同步位置后表变更为s状态,并记录此时的wal位置到pg_subscription_rel.srsublsn

此时srsublsn可能已经到了apply worker同步的前面,所有在commit wal位置小于srsublsn的事务都需要应用。
一旦apply worker追上srsublsn,设置该表为r状态,此时所有订阅范围的表更新事务都需要apply worker应用。

pg_stat_subscription

pg_stat_subscription显示每个订阅worker的状态。一个订阅包含一个apply worker,可选的
还有一个或多个进行初始同步的sync worker。
sync worker上的relid指示正在初始同步的表;对于apply worker,relid为NULL。

apply worker的latest_end_lsn为已反馈给发布端的LSN位置,一定程度上也可以认为是已完成同步的LSN位置。

postgres=# select * from pg_stat_subscription;
 subid | subname |  pid  | relid | received_lsn |      last_msg_send_time       |     last_msg_receipt_time     | latest_end_lsn |      
  latest_end_time        
-------+---------+-------+-------+--------------+-------------------------------+-------------------------------+----------------+------
-------------------------
 18515 | sub1    | 19860 | 18446 |              | 2020-04-24 19:29:10.961417+08 | 2020-04-24 19:29:10.961417+08 |                | 2020-
04-24 19:29:10.961417+08
 18515 | sub1    | 19499 |       | 0/4566B50    | 2020-04-24 19:29:05.946996+08 | 2020-04-24 19:29:05.947017+08 | 0/4566B50      | 2020-
04-24 19:29:05.946996+08
(2 rows)

pg_replication_origin_status

pg_replication_origin_status包含了从复制源增量同步的最后一个位置

postgres=# select * from pg_replication_origin_status;
 local_id | external_id | remote_lsn | local_lsn 
----------+-------------+------------+-----------
        1 | pg_18465    | 0/4540208  | 0/470FFD8
(1 row)

上面的remote_lsn是订阅端应用的最后一个的WAL记录在源节点的开始LSN位置(即执行这条WAL记录的开头)。
如果源节点上后来又产生了其他和订阅无关的WAL记录(比如更新其他表或后台checkpoint产生的WAL),不会反映到pg_replication_origin_status里。

发布端

pg_replication_slots

发布端的pg_replication_slots反映了逻辑订阅复制槽的LSN位点。

postgres=# select * from pg_replication_slots;
-[ RECORD 1 ]-------+----------
slot_name           | sub1
plugin              | pgoutput
slot_type           | logical
datoid              | 13451
database            | postgres
temporary           | f
active              | t
active_pid          | 14058
xmin                | 
catalog_xmin        | 755
restart_lsn         | 0/4540818
confirmed_flush_lsn | 0/4540850
  • restart_lsn
    restart_lsn是可能仍被这个槽的消费者要求的最旧WAL地址(LSN),并且因此不会在检查点期间自动被移除。
  • confirmed_flush_lsn
    confirmed_flush_lsn代表逻辑槽的消费者已经确认接收数据到什么位置的地址(LSN)。比这个地址更旧的数据已经不再可用。

confirmed_flush_lsn是最后一个已同步的WAL记录的结束位置(需要字节对齐,实际是下条WAL的起始位置)。
restart_lsn有时候是最后一个已同步的WAL记录的起始位置。

对应订阅范围内的表的更新WAL记录,必须订阅端执行完这条记录才能算已同步;对其他无关的WAL,直接认为是已同步的,继续处理下一条WAL。

在下面的例子中,我们在订阅端锁住一个订阅表,导致订阅端无法应用这条INSERT WAL,所有confirmed_flush_lsn就暂停在这条WAL前面(0/4540850)。

[postgres@sndsdevdb18 citus]$ pg_waldump worker1/pg_wal/000000010000000000000004 -s 0/045407A8 -n 5
rmgr: XLOG        len (rec/tot):    106/   106, tx:          0, lsn: 0/045407A8, prev 0/04540770, desc: CHECKPOINT_ONLINE redo 0/4540770; tli 1; prev tli 1; fpw true; xid 0:755; oid 24923; multi 1; offset 0; oldest xid 548 in DB 1; oldest multi 1 in DB 1; oldest/newest commit timestamp xid: 555/754; oldest running xid 755; online
rmgr: Standby     len (rec/tot):     50/    50, tx:          0, lsn: 0/04540818, prev 0/045407A8, desc: RUNNING_XACTS nextXid 755 latestCompletedXid 754 oldestRunningXid 755
rmgr: Heap        len (rec/tot):     69/   130, tx:        755, lsn: 0/04540850, prev 0/04540818, desc: INSERT off 2, blkref #0: rel 1663/13451/17988 blk 0 FPW
rmgr: Transaction len (rec/tot):     46/    46, tx:        755, lsn: 0/045408D8, prev 0/04540850, desc: COMMIT 2020-04-24 14:22:20.531476 CST
rmgr: Standby     len (rec/tot):     50/    50, tx:          0, lsn: 0/04540908, prev 0/045408D8, desc: RUNNING_XACTS nextXid 756 latestCompletedXid 755 oldestRunningXid 756

pg_stat_replication

对于一个逻辑订阅,pg_stat_replication中可以看到apply worker的复制状态,其中的write_lsn,flush_lsn,replay_lsnpg_replication_slotsconfirmed_flush_lsn值相同。apply worker的复制的application_name为订阅名。

postgres=# select * from pg_stat_replication ;
  pid  | usesysid | usename  |   application_name    | client_addr | client_hostname | client_port |         backend_start         | bac
kend_xmin |   state   | sent_lsn  | write_lsn | flush_lsn | replay_lsn | write_lag | flush_lag | replay_lag | sync_priority | sync_state
 
-------+----------+----------+-----------------------+-------------+-----------------+-------------+-------------------------------+----
----------+-----------+-----------+-----------+-----------+------------+-----------+-----------+------------+---------------+-----------
-
 19861 |       10 | postgres | sub1_18515_sync_18446 |             |                 |          -1 | 2020-04-24 19:29:10.964055+08 |    
          | startup   |           |           |           |            |           |           |            |             0 | async
 19500 |       10 | postgres | sub1                  |             |                 |          -1 | 2020-04-24 19:26:59.950652+08 |    
          | streaming | 0/4566B50 | 0/4566B50 | 0/4566B50 | 0/4566B50  |           |           |            |             0 | async
(2 rows)

可选的还可能看到sync worker临时创建的用于初始同步的复制。sync worker的复制的application_name为订阅名加上同步表信息。

为了理解sync worker的复制干嘛用的?我们需要先看一下sync worker的处理逻辑。

sync worker初始同步一张表时,分下面几个步骤

  1. 创建临时复制槽,用于sync worker的复制
  2. 从源端copy表数据到目的端
  3. 记录copy完成时的lsn到pg_subscription_rel的srsublsn
  4. 对比srsublsn和apply worker当前同步点lsn(latest_end_lsn)

    4.1 如果srsublsn小于latest_end_lsn,将同步状态改为s
    4.2 如果srsublsn大于latest_end_lsn,通过1的复制槽拉取本表的增量数据,等追上apply worker后,将同步状态改为s
  5. 后续增量同步工作交给apply worker

如何判断订阅已经同步?

在所有表都处于s或r状态时,只要发布端的pg_stat_replication.replay_lsn追上发布端的当前lsn即可。

如果我们通过逻辑订阅进行数据表切换,可以执行以下步骤确保数据同步

  1. 创建订阅并等待所有表完成基本同步

    即所有表在`pg_subscription_rel`中处于s或r状态
  2. 在发布端锁表禁止更新
  3. 获取发布端当前lsn
  4. 获取发布端的replay_lsn(或其他等价指标),如果超过3的lsn,则数据已同步。
  5. 如果尚未同步,重复4

注意点

以下同步位置信息,反映了已处于s或r状态的表的同步位点。

  • pg_replication_slots
  • pg_stat_replication
  • pg_replication_origin_status

对于尚未完成初始同步的表,订阅端copy完初始数据后,会用一个临时的复制槽拉取增量WAL,直到追上apply worker。
追上后修改同步状态为s,后续的增量同步交给apply worker。
因此我们判断订阅的整体复制LSN位置时,必须等所有表都完成初始同步后才有意义。

参考

详细参考:
https://github.com/ChenHuajun/chenhuajun.github.io/blob/master/_posts/2018-07-30-PostgreSQL逻辑订阅处理流程解析.md

相关实践学习
使用PolarDB和ECS搭建门户网站
本场景主要介绍基于PolarDB和ECS实现搭建门户网站。
阿里云数据库产品家族及特性
阿里云智能数据库产品团队一直致力于不断健全产品体系,提升产品性能,打磨产品功能,从而帮助客户实现更加极致的弹性能力、具备更强的扩展能力、并利用云设施进一步降低企业成本。以云原生+分布式为核心技术抓手,打造以自研的在线事务型(OLTP)数据库Polar DB和在线分析型(OLAP)数据库Analytic DB为代表的新一代企业级云原生数据库产品体系, 结合NoSQL数据库、数据库生态工具、云原生智能化数据库管控平台,为阿里巴巴经济体以及各个行业的企业客户和开发者提供从公共云到混合云再到私有云的完整解决方案,提供基于云基础设施进行数据从处理、到存储、再到计算与分析的一体化解决方案。本节课带你了解阿里云数据库产品家族及特性。
相关文章
|
6月前
|
存储 SQL 关系型数据库
【面试题精讲】MySQL逻辑架构
【面试题精讲】MySQL逻辑架构
|
6月前
|
存储 缓存 关系型数据库
高性能Mysql-逻辑架构
高性能Mysql-逻辑架构
|
8月前
|
SQL 缓存 Oracle
第04章 逻辑架构【1.MySQL架构篇】【MySQL高级】2
第04章 逻辑架构【1.MySQL架构篇】【MySQL高级】2
89 0
|
2月前
|
SQL 关系型数据库 分布式数据库
PolarDB for PostgreSQL逻辑复制问题之逻辑复制冲突如何解决
PolarDB for PostgreSQL是基于PostgreSQL开发的一款云原生关系型数据库服务,它提供了高性能、高可用性和弹性扩展的特性;本合集将围绕PolarDB(pg)的部署、管理和优化提供指导,以及常见问题的排查和解决办法。
|
3月前
|
存储 关系型数据库 MySQL
MySQL相关(番外篇)- innodb 逻辑存储结构
MySQL相关(番外篇)- innodb 逻辑存储结构
32 0
|
4月前
|
SQL 存储 缓存
认真学习MySQL的逻辑架构
认真学习MySQL的逻辑架构
41 0
|
4月前
|
存储 人工智能 关系型数据库
postgresql从入门到精通教程 - 第36讲:postgresql逻辑备份
postgresql从入门到精通教程 - 第36讲:postgresql逻辑备份
145 1
|
5月前
|
SQL 存储 缓存
MySQL逻辑架构_逻辑架构的初步了解
MySQL逻辑架构_逻辑架构的初步了解
45 0
|
8月前
|
存储 缓存 关系型数据库
第04章 逻辑架构【1.MySQL架构篇】【MySQL高级】3
第04章 逻辑架构【1.MySQL架构篇】【MySQL高级】3
61 0
|
8月前
|
SQL 存储 缓存
第04章 逻辑架构【1.MySQL架构篇】【MySQL高级】1
第04章 逻辑架构【1.MySQL架构篇】【MySQL高级】1
57 0