29.7. 序列复制 #
要将序列从发布端同步到订阅端,首先使用
CREATE PUBLICATION ... FOR ALL SEQUENCES
将它们发布,然后在订阅端执行:
使用
CREATE SUBSCRIPTION,并设置copy_data = true(默认值),从发布端复制初始序列值。使用
ALTER SUBSCRIPTION ... REFRESH PUBLICATION仅同步新添加的序列。使用
ALTER SUBSCRIPTION ... REFRESH SEQUENCES重新同步当前订阅已知的全部序列。
执行上述任一订阅端命令后,都会启动一个序列同步工作进程,并在序列同步完成后退出。
能否启动序列同步工作进程受
max_sync_workers_per_subscription
配置项限制。
注意
序列同步要求发布端运行 PostgreSQL 19 或更高版本。
29.7.1. 序列定义不匹配 #
序列同步工作进程会验证发布端和订阅端之间的序列定义是否一致。如果存在不匹配,工作进程会记录指出这些不匹配的错误并退出。应用工作进程会持续重新启动序列同步工作进程,直到同步成功。另见 wal_retrieve_retry_interval。
要解决该问题,请使用
ALTER SEQUENCE
将订阅端的序列参数调整为与发布端一致。
29.7.2. 重新同步失配的序列 #
随着发布端推进,订阅端的序列值会变得不同步。
可以将订阅端上的
pg_subscription_rel.srsublsn
与发布端序列上的
pg_get_sequence_data
函数返回的 page_lsn 进行比较,以检测这种不同步。如果需要,再执行
ALTER SUBSCRIPTION ... REFRESH SEQUENCES
重新同步。
警告
每个序列在生成新的 WAL 记录之前,都会先在内存中缓存一批值(通常为 32 个),因此只有在整个缓存批次用尽后,其 LSN 才会前进。结果是,如果序列增量仍落在同一个缓存批次内(通常是 32 个值),就无法通过 LSN 比较检测到序列值漂移。
29.7.3. 示例 #
在发布端创建一些序列。
/* pub # */ CREATE SEQUENCE s1 START WITH 10 INCREMENT BY 1; /* pub # */ CREATE SEQUENCE s2 START WITH 100 INCREMENT BY 10;
在订阅端创建相同的序列。
/* sub # */ CREATE SEQUENCE s1 START WITH 10 INCREMENT BY 1; /* sub # */ CREATE SEQUENCE s2 START WITH 100 INCREMENT BY 10;
在发布端多次推进这些序列。
/* pub # */ SELECT nextval('s1');
nextval
---------
10
(1 row)
/* pub # */ SELECT nextval('s1');
nextval
---------
11
(1 row)
/* pub # */ SELECT nextval('s2');
nextval
---------
100
(1 row)
/* pub # */ SELECT nextval('s2');
nextval
---------
110
(1 row)
检查发布端上的序列页 LSN。
/* pub # */ SELECT * FROM pg_get_sequence_data('s1');
last_value | is_called | page_lsn
------------+-----------+------------
11 | t | 0/0178F9E0
(1 row)
/* pub # */ SELECT * FROM pg_get_sequence_data('s2');
last_value | is_called | page_lsn
------------+-----------+------------
110 | t | 0/0178FAB0
(1 row)
为这些序列创建发布。
/* pub # */ CREATE PUBLICATION pub1 FOR ALL SEQUENCES;
订阅该发布。
/* sub # */ CREATE SUBSCRIPTION sub1 /* sub - */ CONNECTION 'host=localhost dbname=test_pub application_name=sub1' /* sub - */ PUBLICATION pub1;
验证初始序列值已同步。
/* sub # */ SELECT last_value, is_called FROM s1;
last_value | is_called
------------+-----------
11 | t
(1 row)
/* sub # */ SELECT last_value, is_called FROM s2;
last_value | is_called
------------+-----------
110 | t
(1 row)
确认发布端上的序列页 LSN 已记录到订阅端。
/* sub # */ SELECT srrelid::regclass, srsublsn FROM pg_subscription_rel; srrelid | srsublsn ---------+------------ s1 | 0/0178F9E0 s2 | 0/0178FAB0 (2 rows)
再在发布端推进这些序列 50 次。
/* pub # */ SELECT nextval('s1') FROM generate_series(1,50);
/* pub # */ SELECT nextval('s2') FROM generate_series(1,50);
再次检查发布端上的序列页 LSN。
/* pub # */ SELECT * FROM pg_get_sequence_data('s1');
last_value | is_called | page_lsn
------------+-----------+------------
61 | t | 0/017CED28
(1 row)
/* pub # */ SELECT * FROM pg_get_sequence_data('s2');
last_value | is_called | page_lsn
------------+-----------+------------
610 | t | 0/017CEDF8
(1 row)
发布端和订阅端的序列页 LSN 不同,说明这些序列已经失配。使用
ALTER SUBSCRIPTION ... REFRESH SEQUENCES
重新同步订阅端已知的全部序列。
/* sub # */ ALTER SUBSCRIPTION sub1 REFRESH SEQUENCES;
再次检查订阅端上的序列。
/* sub # */ SELECT last_value, is_called FROM s1;
last_value | is_called
------------+-----------
61 | t
(1 row)
/* sub # */ SELECT last_value, is_called FROM s2;
last_value | is_called
------------+-----------
610 | t
(1 row)