it-swarm.com.de

Postgres-Sequenzprobleme, manuell erstellte Pids und korrektes Zurücksetzen der Sequenz

Entwickler sind im letzten Jahr auf Probleme mit Sequenzen in unserer Postgres 9.6-Datenbank gestoßen, bei denen sie nicht mehr funktionieren. Daher haben sie beispielsweise auf das Einfügen von Zeilen mit einer manuell erstellten PID basierend auf der PID des zuletzt eingefügten Datensatzes und das Zurücksetzen der Sequenz mithilfe dieses Codes zurückgegriffen:

SELECT pg_catalog.setval(pg_get_serial_sequence('mytable', 'pid'), 
       (SELECT MAX(pid)+1 FROM mytable) );

Ich habe genug Erfahrung, um zu wissen, dass wir uns beim Erstellen unserer IDs auf die Datenbank verlassen können sollten und dass das Erstellen eigener PIDs auf der Grundlage des letzten Maximums weder für alle Szenarien eine bewährte Methode noch sicher ist, obwohl dies im Allgemeinen funktioniert hat unser täglicher Gebrauch.

Der Grund, warum sie beim ersten Mal nicht mehr arbeiteten, ist die bekannte Ursache, dass beispielsweise eine Tabelle wiederhergestellt wurde. An diesem Punkt denke ich jedoch, dass der manuelle Code und die Sequenzen aufeinander treten und der Code zum Zurücksetzen der Sequenzen nicht solide ist. Es scheint offensichtlich, dass das Einfügen des letzten Maximums die Sequenz untergräbt, die einfach ihre eigene Nummer hat, die sie erhöht.

Während ich lese, möchte ich wissen, ob mich heute jemand in die richtige Richtung weist, um die Sequenz wiederherzustellen und fehlerfrei an einer vorhandenen Tabelle zu arbeiten - und sie funktionieren zu lassen, obwohl Code vorhanden ist, der manchmal eingefügt wird eine PID aus Code, basierend auf den letzten max. (Langfristig bin ich mir natürlich völlig bewusst, dass es am besten ist, wenn der gesamte Code gelöscht wird - aber gibt es eine Möglichkeit, diesen Code vorerst zu umgehen?)

In einer Lösung wäre eine Möglichkeit in PostgreSQL 9.6 enthalten, die Sequenz selbst zurückzusetzen, wenn es einen Konflikt gibt - nicht sicher, ob dies möglich ist, und mich auf Vorträge von erfahrenen Personen vorzubereiten -, aber deshalb bin ich hier!

Schließlich - und dies ist eine beunruhigende Tatsache, die mich hierher gebracht hat - sehe ich nach dem Zurücksetzen der Sequenz in pg admin zwei pids in der Tabelle in pg admin 3 und 4, die auch im Erstellungsskript angezeigt werden. Die 'zweite' PID-Spalte wird nicht angezeigt, wenn\d in psql ausgeführt wird, was gut ist - aber ich dachte, es könnte relevant sein.

UPDATE - In diesem letzten Punkt wurden die Ghost-Duplikat-PIDs für eine Tabelle in pg admin verursacht, weil zwei Sequenzen für dieselbe Spalten-/Tabellenkombination vorhanden waren. Die zweite wurde irgendwann erstellt, um zu versuchen, fehlerhafte Sequenzen im Laufe der Zeit zu beheben (z. B. mytable_pid_seq und mytable_pid_seq1). Ich bin mir nicht sicher, warum/wie dies in der Datenbank geschehen durfte.

6
csdev

In einer Lösung wäre eine Möglichkeit in PostgreSQL 9.6 enthalten, die Sequenz selbst zurückzusetzen, wenn es einen Konflikt gibt - nicht sicher, ob dies möglich ist, und mich auf Vorträge von erfahrenen Personen vorzubereiten -, aber deshalb bin ich hier!

Erstens wäre die eigentliche Lösung, dass der Code immer die Sequenz anstelle der inkonsistenten Mischung aus select 1+max(pk) und nextval('seqname') verwendet.

Abgesehen davon können Sie als Band-Aid-Lösung vor einem echten Fix einen Trigger für INSERT für jede Zeile haben, die immer nextval für die Sequenz aufruft, sodass Sie sicher sind, dass die Sequenz niemals zurückbleibt , auch wenn dem INSERT selbst ein nextval -Aufruf fehlt.

Wenn die Spalte eine DEFAULT nextval('seqname') hat (entweder manuell oder über eine SERIAL -Deklaration festgelegt) und ein Trigger zusätzlich zu nextval der nextval aufruft DEFAULT, das die Sequenz nur um zwei statt um eins vorverlegt. Dies sollte für Ihre App kein Problem sein, da Sequenzen aufgrund von Rollback oder Caching ohnehin Löcher aufweisen können.

Das Aufrufen von setval mit SELECT 1+max(pk) from table funktioniert ebenfalls, jedoch nur, wenn keine anderen Transaktionen gleichzeitig die Sequenz verwenden. Das in einem Trigger zu tun, klingt also nicht nach einer guten Idee. Normalerweise wird diese Art der Anpassung immer erst nach einer Massenladung durchgeführt (genau das ist es, was pg_dump erzeugt, wenn die Sequenz der Tabelle gehört).

1
Daniel Vérité

Der Grund, warum sie beim ersten Mal nicht mehr arbeiteten, ist die bekannte Ursache, dass beispielsweise eine Tabelle wiederhergestellt wurde. An diesem Punkt denke ich jedoch, dass der manuelle Code und die Sequenzen aufeinander treten und der Code zum Zurücksetzen der Sequenzen nicht solide ist. Es scheint offensichtlich, dass das Einfügen des letzten Maximums die Sequenz untergräbt, die einfach ihre eigene Nummer hat, die sie erhöht.

Wenn Sie von pg_dump Wiederherstellen, kann dies auf keinen Fall das Problem sein. Nimm zum Beispiel,

CREATE TABLE foo (id serial PRIMARY KEY);
INSERT INTO foo DEFAULT VALUES;

Dies erzeugt eine Tabelle wie folgt:

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

Sie können hier sehen, dass der ID-Typ nur int ist und die Sequenz standardmäßig nextval ist. Wenn Sie pg_dump Verwenden, wird ein solcher Speicherauszug generiert (Entfernen von Kommentaren und Berechtigungen).

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);

Das ist eine Menge Zeug, aber es ist der sicherste Weg, selbst eine einfache Tabelle wiederherzustellen. Beachten Sie hier also den Aufruf von pg_catalog.setval

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

Nachdem Sie andere potenziell höhere pkids zusammengeführt haben , müssen Sie dies mit dem neuen höchsten Wert aufrufen.

SELECT pg_catalog.setval(
  'foo_id_seq',
  (SELECT max(id) FROM foo),
  true
);

Dann Viola, du bist wieder an der Arbeit. Sie sind überhaupt nicht kaputt.

Wichtiger Hinweis, dies gilt für serial Typen. Seite 10 ändert diese Schnittstelle, wenn Sie Identitätsspalten verwenden, und es wird stattdessen ein standardisierter ALTER TABLE Befehl sein, um den aktuellen Wert zu ändern.

2
Evan Carroll