PostgreSQL
 sql >> Datenbank >  >> RDS >> PostgreSQL

Isolationsstufe SERIALIZABLE in Spring-JDBC

TL;DR:Die Erkennung von Serialisierbarkeitskonflikten wurde in Pg 9.1 dramatisch verbessert, also aktualisieren Sie.

Es ist schwierig, aus Ihrer Beschreibung herauszufinden, was das eigentliche SQL ist und warum Sie ein Rollback erwarten. Es sieht so aus, als hätten Sie die serialisierbare Isolation ernsthaft missverstanden, vielleicht denken Sie, dass sie alle Prädikate perfekt testet, was nicht der Fall ist, insbesondere nicht in Pg 8.4.

SERIALIZABLE garantiert nicht perfekt, dass die Transaktionen so ausgeführt werden, als ob sie in Serie ausgeführt würden - da dies aus Performance-Sicht unerschwinglich teuer wäre, wenn es überhaupt möglich wäre. Es bietet nur eine begrenzte Überprüfung. Was genau überprüft wird und wie, variiert von Datenbank zu Datenbank und von Version zu Version, daher müssen Sie die Dokumentation für Ihre Version Ihrer Datenbank lesen.

Anomalien sind möglich, wenn zwei Transaktionen in SERIALIZABLE ausgeführt werden -Modus ein anderes Ergebnis liefern, als wenn diese Transaktionen wirklich nacheinander ausgeführt würden.

Lesen Sie die Dokumentation zur Transaktionsisolation in Pg, um mehr zu erfahren. Beachten Sie, dass SERIALIZABLE hat das Verhalten in Pg 9.1 dramatisch verändert, also stellen Sie sicher, dass Sie die Version des Handbuchs lesen, die für Ihre Pg-Version geeignet ist. Hier ist die Version 8.4 . Lesen Sie insbesondere 13.2.2.1. Serialisierbare Isolierung versus echte Serialisierbarkeit . Vergleichen Sie das jetzt mit der stark verbesserten Serialisierungsunterstützung auf Basis von Prädikatsperren beschrieben in der Seite 9.1 docs .

Es sieht so aus, als ob Sie versuchen, eine Logik wie diesen Pseudocode auszuführen:

count = query("SELECT count(*) FROM the_table");
if (count < threshold):
    query("INSERT INTO the_table (...) VALUES (...)");

Wenn ja, wird das in Pg 8.4 nicht funktionieren, wenn es gleichzeitig ausgeführt wird - es ist ziemlich dasselbe wie das Anomalie-Beispiel, das in der oben verlinkten Dokumentation verwendet wird. Erstaunlicherweise funktioniert es tatsächlich auf Pg 9.1; Ich habe nicht einmal erwartet, dass die Prädikatsperre von 9.1 die Verwendung von Aggregaten erfasst.

Du schreibst das:

aber 8.4 erkennt nicht, dass die beiden Transaktionen voneinander abhängig sind, etwas, das Sie trivial beweisen können, indem Sie zwei psql verwenden Sitzungen, um es zu testen. Nur mit dem in 9.1 eingeführten Zeug zur echten Serialisierbarkeit wird dies funktionieren - und ehrlich gesagt war ich überrascht, dass es in 9.1 funktioniert.

Wenn Sie in Pg 8.4 so etwas wie eine maximale Zeilenanzahl erzwingen möchten, müssen Sie LOCK die Tabelle um gleichzeitiges INSERT zu verhindern s, das Sperren entweder manuell oder über eine Trigger-Funktion . Wenn Sie dies in einem Trigger tun, ist von Natur aus eine Lock-Promotion erforderlich, wodurch häufig ein Deadlock auftritt, die Aufgabe jedoch erfolgreich ausgeführt wird. Dies geschieht besser in der Anwendung, in der Sie LOCK TABLE my_table IN EXCLUSIVE MODE ausgeben können bevor Sie sogar SELECT erhalten ing aus der Tabelle, so dass es bereits den höchsten Sperrmodus hat, den es für die Tabelle benötigt, und daher keine Deadlock-anfällige Sperrenförderung benötigen sollte. Der EXCLUSIVE Der Sperrmodus ist geeignet, da er SELECT zulässt s, aber sonst nichts.

So testen Sie es in zwei psql-Sitzungen:

SESSION 1                               SESSION 2

create table ser_test( x text );

BEGIN TRANSACTION 
ISOLATION LEVEL SERIALIZABLE;


                                        BEGIN TRANSACTION 
                                        ISOLATION LEVEL SERIALIZABLE;

SELECT count(*) FROM ser_test ;

                                        SELECT count(*) FROM ser_test ;

INSERT INTO ser_test(x) VALUES ('bob');


                                        INSERT INTO ser_test(x) VALUES ('bob');

 COMMIT;

                                        COMMIT;

Bei Ausführung auf Pg 9.1 ist der st commits succeeds then the second COMMIT` schlägt fehl mit:

regress=# COMMIT;
ERROR:  could not serialize access due to read/write dependencies among transactions
DETAIL:  Reason code: Canceled on identification as a pivot, during commit attempt.
HINT:  The transaction might succeed if retried.

aber wenn sie auf 8.4 ausgeführt werden, sind beide Commits erfolgreich, da 8.4 nicht den gesamten Prädikat-Sperrcode für die Serialisierbarkeit in 9.1 hinzugefügt hatte.