这是因为 PostgreSQL 中序列的工作方式:
从源头看这张表,有一个序列:
src=# CREATE TABLE s (a serial PRIMARY KEY, b text);
CREATE TABLE
src=# INSERT INTO s (b) VALUES ('foo'), ('bar'), ('baz');
INSERT 0 3
src=# SELECT * FROM s;
a | b
---+-----
1 | foo
2 | bar
3 | baz
(3 rows)
src=# SELECT currval('s_a_seq'), nextval('s_a_seq');
currval | nextval
---------+---------
3 | 4
(1 row)
创建序列 s_a_seq 是为了支持串行类型的 a 列。这会生成 s.a. 的自动递增值。现在让我们将它复制到 dst 中,并插入另一行:
dst=# SELECT * FROM s;
a | b
---+-----
1 | foo
2 | bar
3 | baz
(3 rows)
dst=# INSERT INTO s (b) VALUES ('foobaz');
ERROR: duplicate key value violates unique constraint "s_pkey"
DETAIL: Key (a)=(1) already exists.
dst=# SELECT currval('s_a_seq'), nextval('s_a_seq');
currval | nextval
---------+---------
1 | 2
(1 row)
糟糕,刚刚发生了什么?目的地尝试从头开始序列并为 a 生成值 1。这是因为逻辑复制不会复制序列的值,因为序列的下一个值没有存储在表本身中。
解决方法
如果你从逻辑上考虑,你不能在没有双向同步的情况下从两个地方修改相同的“自动增量”值。如果您确实需要在表的每一行中增加一个数字,并且需要从多个服务器插入到该表中,您可以:
- 为号码使用外部来源,例如 ZooKeeper 或 etcd,
- 使用不重叠的范围 - 例如,第一个服务器生成并插入 1 到 100 万范围内的数字,第二个服务器生成并插入 100 万到 200 万范围内的数字,依此类推。