postgres 序列问题、手动创建的 pid 和正确的序列重置

csd*_*dev 8 postgresql sequence postgresql-9.6

在过去的一年里,开发人员在我们的 postgres 9.6 数据库中遇到了序列问题,他们停止工作,因此他们采取了诸如根据最后插入的记录的 pid 手动创建的 pid 插入行,并使用以下代码重置序列的方法:

SELECT pg_catalog.setval(pg_get_serial_sequence('mytable', 'pid'), 
       (SELECT MAX(pid)+1 FROM mytable) );
Run Code Online (Sandbox Code Playgroud)

我有足够的经验,知道我们应该能够依靠数据库来创建我们的 id,并且基于最后一个最大值创建自己的 PID 不是“最佳实践”,也不是对所有场景都安全,尽管它通常适用于我们的日常使用。

他们第一次停止工作的原因是众所周知的原因,例如表被恢复。但是,此时我认为手动代码和序列是相互踩踏的,序列重置代码并不可靠。很明显,插入最后一个最大值会破坏序列,它只会增加自己的数字。

当我在阅读时,我想知道今天是否有人指出我正确的方向以恢复序列并在现有表上完美工作 - 即使代码到位,有时也会插入来自代码的 pid,基于最后一个最大值。(当然,从长远来看,我完全意识到最好删除所有此类代码 - 但现在有没有办法解决该代码?)

包含在解决方案中的将是 postgreSQL 9.6 中的一种方法,如果发生冲突,它可以自行重置序列 - 不确定是否可能,并准备接受更有经验的人的讲座 - 但这就是我在这里的原因!

最后——这是让我来到这里的一个令人不安的事实——在 pg admin 中重置序列后,我在 pg admin 3 和 4 的表中看到两个 pid,它们也显示在创建脚本中。在 psql 中执行 \d 时不显示“第二个”PID 列,这很好 - 但我认为它可能是相关的。

更新 - 在最后一点上,导致 pg admin 中表的幽灵重复 PID 是因为同一列/表组合有两个序列,第二个是在某个时间创建的,以尝试修复损坏的序列(例如 mytable_pid_seq 和mytable_pid_seq1)。不知道为什么/如何允许在数据库中发生这种情况。

Eva*_*oll 5

他们第一次停止工作的原因是众所周知的原因,例如表被恢复。但是,此时我认为手动代码和序列是相互踩踏的,序列重置代码并不可靠。很明显,插入最后一个最大值会破坏序列,它只会增加自己的数字。

如果您正在从pg_dump那里恢复,则不可能是问题所在。举个例子,

CREATE TABLE foo (id serial PRIMARY KEY);
INSERT INTO foo DEFAULT VALUES;
Run Code Online (Sandbox Code Playgroud)

这会生成一个这样的表,

                         Table "public.foo"
 Column |  Type   |                    Modifiers                     
--------+---------+--------------------------------------------------
 id     | integer | not null default nextval('foo_id_seq'::regclass)
Indexes:
    "foo_pkey" PRIMARY KEY, btree (id)
Run Code Online (Sandbox Code Playgroud)

你可以在这里看到 id 的类型是 just int,所有序列所做的都是默认为nextval。使用pg_dump,这将生成这样的转储(删除评论和权限)

CREATE TABLE foo (
    id integer NOT NULL
);

CREATE SEQUENCE foo_id_seq
    START WITH 1
    INCREMENT BY 1
    NO MINVALUE
    NO MAXVALUE
    CACHE 1;

ALTER TABLE ONLY foo ALTER COLUMN id SET DEFAULT nextval('foo_id_seq'::regclass);

COPY foo (id) FROM stdin;
1
\.

SELECT pg_catalog.setval('foo_id_seq', 1, true);

ALTER TABLE ONLY foo
    ADD CONSTRAINT foo_pkey PRIMARY KEY (id);
Run Code Online (Sandbox Code Playgroud)

有很多东西,但这是恢复即使是简单表的最安全方法。所以请注意这里的调用pg_catalog.setval

SELECT pg_catalog.setval('foo_id_seq', 1, true);
Run Code Online (Sandbox Code Playgroud)

在合并其他可能更高的 pkid 后,您必须使用新的最高值调用它。

SELECT pg_catalog.setval(
  'foo_id_seq',
  (SELECT max(id) FROM foo),
  true
);
Run Code Online (Sandbox Code Playgroud)

那么中提琴,你又开始工作了。他们根本没有坏。

重要说明,这是针对serial类型的。如果您使用标识列,第 10 页将更改此界面,而它将成为ALTER TABLE更改当前值的标准化命令。


Dan*_*ité 3

解决方案中包含了 postgreSQL 9.6 中的一种方法,如果存在冲突,它可以重置序列本身 - 不确定是否可能,并且我准备接受更有经验的讲座 - 但这就是我在这里的原因!

首先,真正的解决方法是代码始终使用序列而不是不一致的select 1+max(pk)和组合nextval('seqname')

话虽这么说,作为真正修复之前的创可贴解决方案,您可以为始终调用nextval序列的每一行在 INSERT 上设置一个触发器,这样您就可以确保序列永远不会落后,即使 INSERT 本身也如此未接nextval来电。

如果该列有 a DEFAULT nextval('seqname')(手动设置或通过声明设置),并且除了the 之外SERIAL还有一个触发器调用,则只会将序列前进 2 而不是 1。对于您的应用程序来说,这不应该成为问题,因为由于回滚或缓存,序列无论如何都可能存在漏洞。nextvalnextvalDEFAULT

调用setvalwithSELECT 1+max(pk) from table也可以,但前提是没有其他事务同时使用该序列。因此,在触发器中执行此操作听起来不是一个好主意。通常,这种调整仅在批量加载之后进行(事实上,这就是pg_dump当序列归表所有时产生的结果)。