要将序列从发布端同步到订阅端,首先使用 CREATE PUBLICATION ... FOR ALL SEQUENCES 将它们发布,然后在订阅端执行:
使用 CREATE SUBSCRIPTION 初始同步已发布的序列。
使用 ALTER SUBSCRIPTION ... REFRESH PUBLICATION 仅同步新添加的序列。
使用 ALTER SUBSCRIPTION ... REFRESH SEQUENCES 重新同步当前订阅已知的全部序列。
执行上述任一订阅端命令后,都会启动一个 序列同步工作者,并在序列同步完成后退出。
启动序列同步工作者的数量受 max_sync_workers_per_subscription 配置项限制。
序列同步工作者会验证发布端和订阅端之间的序列定义是否一致。如果存在不匹配, 工作者会记录错误并退出。应用工作者会持续重新启动序列同步工作者,直到同步成功。 另见 wal_retrieve_retry_interval。
要解决该问题,请使用 ALTER SEQUENCE 将订阅端的序列参数调整为与发布端一致。
随着发布端推进,订阅端的序列值会变得不同步。
可以将订阅端上的 pg_subscription_rel.srsublsn 与发布端序列上的 pg_get_sequence_data 函数返回的 page_lsn 进行比较,以检测这种不同步。如果需要, 再执行 ALTER SUBSCRIPTION ... REFRESH SEQUENCES 重新同步。
每个序列在生成新的 WAL 记录之前,都会先在内存中缓存一批值(通常为 32 个), 因此只有在整个缓存批次用尽后,其 LSN 才会前进。结果是,如果序列增量仍落在同一 个缓存批次内(通常是 32 个值),就无法通过 LSN 比较检测到序列值漂移。
在发布端创建一些序列。
/* 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)