Aus dem Transaktionsisolation Seite:
Ein EXPLAIN
darauf SELECT
kann Ihnen mitteilen, welcher Abfrageplan verwendet wird, aber wenn die Tabelle klein (oder leer!) ist, wird PostgreSQL mit ziemlicher Sicherheit einen sequentiellen Scan auswählen, anstatt auf den Index zu verweisen. Dies führt zu einer Prädikatsperre für die gesamte Tabelle, was zu einem Serialisierungsfehler führt, wenn eine andere Transaktion irgendetwas mit der Tabelle macht.
Auf meinem System:
isolation=# EXPLAIN SELECT * from mydevice where cid = 1;
QUERY PLAN
----------------------------------------------------------
Seq Scan on mydevice (cost=0.00..23.38 rows=5 width=46)
Filter: (cid = 1)
(2 rows)
Sie könnten versuchen, einen Index hinzuzufügen und ihn dazu zu zwingen, diesen zu verwenden:
isolation=# CREATE INDEX mydevice_cid_key ON mydevice (cid);
CREATE INDEX
isolation=# SET enable_seqscan = off;
SET
isolation=# EXPLAIN SELECT * from mydevice where cid = 1;
QUERY PLAN
----------------------------------------------------------------------------------
Index Scan using mydevice_cid_key on mydevice (cost=0.00..8.27 rows=1 width=46)
Index Cond: (cid = 1)
(2 rows)
Dies ist jedoch nicht die richtige Lösung. Gehen wir ein wenig zurück.
Serializable soll garantieren, dass Transaktionen genau den gleichen Effekt haben, als würden sie nacheinander ausgeführt, obwohl Sie diese Transaktionen tatsächlich gleichzeitig ausführen. PostgreSQL verfügt nicht über unendliche Ressourcen. Es stimmt also, dass es Prädikatsperren auf Daten setzt, auf die Ihre Abfrage tatsächlich zugreift, aber „Daten“ kann mehr bedeuten als „zurückgegebene Zeilen“.
PostgreSQL kennzeichnet Serialisierungsfehler, wenn es der Meinung ist, dass ein Problem vorliegen könnte, und nicht, wenn es sicher ist. (Daher werden Zeilensperren auf Seitensperren verallgemeinert.) Diese Designauswahl führt zu Fehlalarmen, wie z. B. in Ihrem Beispiel. Falsch positive Ergebnisse sind alles andere als ideal, wirken sich jedoch nicht auf die Korrektheit der Isolationssemantik aus.
Die Fehlermeldung lautet:
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.
Dieser Hinweis ist der Schlüssel. Ihre Anwendung muss Serialisierungsfehler abfangen und den gesamten Vorgang wiederholen . Dies gilt immer dann, wenn SERIALIZABLE
im Spiel ist - es garantiert die serielle Korrektheit trotz Parallelität, aber es kann dies nicht ohne die Hilfe Ihrer Anwendung tun. Anders ausgedrückt:Wenn Sie tatsächlich gleichzeitig Änderungen vornehmen, kann PostgreSQL die Isolationsanforderungen nur erfüllen, indem Sie Ihre Anwendung auffordern, sich selbst zu serialisieren. Also: