RDS PostgreSQL支持在只读实例上创建本地逻辑复制槽,下游订阅者可以直接连接只读实例消费变更,从而将逻辑复制的消费流量从主实例转移到只读实例,减轻主实例的负载。
前提条件
PostgreSQL 16或以上版本,且内核小版本不低于20260530。如提示不支持,请升级内核小版本,具体操作,请参见升级内核小版本。
背景信息
PostgreSQL 原生逻辑复制槽(Logical Replication Slot)在主实例上创建,下游订阅者必须连接主实例消费变更。RDS PostgreSQL 支持在只读实例上创建本地逻辑复制槽,使下游订阅者可以直接连接只读实例消费变更,以减轻主实例负载。逻辑复制槽的更多信息,请参见PostgreSQL官方文档。
注意事项
在只读实例上创建的逻辑复制槽为本地 slot,不参与主备故障转移。只读实例重搭(重建)后,这些 slot 将丢失,需要在重搭后重新创建。
建议仅在非高可用的只读实例上使用此方式。如果只读实例承担高可用读流量,slot 丢失可能导致下游订阅中断,需评估业务影响。
使用示例
以下示例假设已有主实例(RW)和只读实例(RO),且只读实例通过流复制与主实例保持数据同步。本文介绍以下三种典型使用方式:
基本用法:在只读实例上手动创建并消费slot(使用
test_decoding插件),适用于自定义解码逻辑场景。通过Subscription订阅只读实例(自动创建slot):最典型的使用场景,由
CREATE SUBSCRIPTION自动在只读实例上创建slot。通过Subscription订阅只读实例(手动预创建slot):进阶用法,先手动创建slot再建立订阅,确保不丢失建立期间的变更。
基本用法:手动创建并消费slot
本示例演示如何在只读实例上手动创建逻辑复制槽,并通过test_decoding插件消费主实例的数据变更。
在只读实例上创建本地逻辑复制槽。
-- [RO] 创建本地logical slot SELECT pg_create_logical_replication_slot('my_slot', 'test_decoding');确认slot创建成功。
-- [RO] 确认slot创建成功 SELECT slot_name, plugin FROM pg_replication_slots WHERE slot_name = 'my_slot';查询结果:
slot_name | plugin -----------+--------------- my_slot | test_decoding (1 row)在主实例上写入数据。
-- [RW] 主库写入数据 INSERT INTO my_table VALUES (1), (2), (3);等待WAL回放到只读实例。
在主实例上查询当前WAL位置:
-- [RW] 获取主库当前WAL位置 SELECT pg_current_wal_lsn();在只读实例上查询回放进度,直到回放位置不小于主实例的WAL位置:
-- [RO] 查看只读实例回放进度 SELECT pg_last_wal_replay_lsn();说明PostgreSQL 15及以上版本,也可以使用
pg_wal_replay_wait(target_lsn)函数主动等待回放至指定LSN,无需轮询。在只读实例上通过slot消费变更。
-- [RO] 通过slot消费变更 SELECT * FROM pg_logical_slot_get_changes('my_slot', NULL, NULL);查询结果:
lsn | xid | data -----------+-----+---------------------------------------- 0/XXXXXXX | 123 | table public.my_table: INSERT: ... (3 rows)不再需要时,删除slot。
-- [RO] 删除slot SELECT pg_drop_replication_slot('my_slot');
通过Subscription订阅只读实例(自动创建slot)
这是最典型的使用场景:下游订阅实例(Subscriber)直接连接只读实例,由CREATE SUBSCRIPTION自动在只读实例上创建slot。
在主实例上创建表和发布。
-- [RW] 主库上创建表和publication CREATE TABLE tab_rep (id int PRIMARY KEY, data text); CREATE PUBLICATION my_pub FOR TABLE tab_rep;等待WAL回放到只读实例,确保publication在只读实例上可见。等待方法请参见"基本用法"中的步骤4。
在下游订阅实例上创建相同的表结构。
-- [Subscriber] 下游实例创建表结构 CREATE TABLE tab_rep (id int PRIMARY KEY, data text);在下游订阅实例上创建订阅,连接只读实例。
-- [Subscriber] 创建subscription,连接只读实例 -- subscription会自动在只读实例上创建名为'my_sub'的slot CREATE SUBSCRIPTION my_sub CONNECTION 'host=<RO_HOST> port=<RO_PORT> dbname=postgres' PUBLICATION my_pub WITH (copy_data = off);说明此操作会在只读实例上自动创建一个逻辑复制槽。该slot为只读实例本地slot,不参与故障转移,只读实例重搭后会丢失。
在主实例上写入数据并验证同步。
-- [RW] 主库写入数据 INSERT INTO tab_rep VALUES (1, 'hello'), (2, 'world');数据流向:RW →(流复制/WAL)→ RO →(逻辑复制)→ Subscriber。
-- [Subscriber] 验证数据已同步 SELECT * FROM tab_rep;查询结果:
id | data ----+------- 1 | hello 2 | world (2 rows)不再需要时,在下游实例上删除订阅(会自动删除只读实例上的slot)。
-- [Subscriber] 删除subscription,自动清理只读实例上的slot DROP SUBSCRIPTION my_sub;
通过Subscription订阅只读实例(手动预创建slot)
如果需要先创建slot再建立订阅(例如确保不丢失建立期间的变更),可以手动预创建slot。
在只读实例上手动创建slot。
-- [RO] 先在只读实例上手动创建slot SELECT pg_create_logical_replication_slot('my_sub', 'pgoutput');在下游订阅实例上创建订阅,使用已有的slot。
-- [Subscriber] 创建subscription,使用已有的slot,不再自动创建 CREATE SUBSCRIPTION my_sub CONNECTION 'host=<RO_HOST> port=<RO_PORT> dbname=postgres' PUBLICATION my_pub WITH (copy_data = off, create_slot = false);说明通过指定
create_slot = false,CREATE SUBSCRIPTION将直接使用只读实例上已有的slot,不会重复创建。后续使用方式与"通过Subscription订阅只读实例(自动创建slot)"相同。