PostgreSQL-Fehler: could not serialize access
ERROR: could not serialize access due to concurrent update
-- oppure, a livello Serializable:
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 Fehler existiert nur auf den Isolationsebenen Repeatable Read und Serializable, und es sind diese Ebenen, die wie vorgesehen arbeiten: PostgreSQL konnte kein Ergebnis erzeugen, das mit den von Ihnen geforderten Garantien konsistent ist, und hat daher Ihre Transaktion abgebrochen, statt stillschweigend etwas Falsches zurückzugeben.
Was dieser Fehler bedeutet
Die beiden Varianten entsprechen den beiden Ebenen:
- "due to concurrent update" (Repeatable Read): Ihre Transaktion hat versucht, eine Zeile zu ändern, die eine andere Transaktion nachdem Ihr Snapshot erstellt wurde geändert und committet hat. PostgreSQL kann Ihren Schreibvorgang nicht auf Daten anwenden, die Sie nie gesehen haben, und bricht Sie daher ab.
- "due to read/write dependencies among transactions" (Serializable): Die SSI-Maschinerie fand ein Muster von Lese- und Schreibvorgängen unter gleichzeitigen Transaktionen, das keiner nacheinander ausgeführten Reihenfolge entsprechen konnte. Beachten Sie die Formulierung in der Doku und in der Praxis: Serializable kann Transaktionen vorbeugend abbrechen — gelegentliche False Positives gehören dazu.
In jedem Fall wurde die Transaktion zurückgerollt und würde, wie der HINT sagt, wahrscheinlich erfolgreich sein, wenn man sie einfach erneut ausführt. SQLSTATE ist in beiden Fällen 40001.
Häufige Ursachen
- Ausführung bei Repeatable Read oder Serializable mit gleichzeitigen Schreibern auf denselben Zeilen — heiße Zeilen und Zähler sind die üblichen Kollisionspunkte.
- Lange Transaktionen: je älter Ihr Snapshot, desto wahrscheinlicher hat inzwischen jemand eine widersprüchliche Änderung committet.
- Bei Serializable Queries, die breit lesen: ein sequenzieller Scan nimmt einen Prädikat-Lock auf die gesamte Tabelle, kollidiert also mit jedem gleichzeitigen Schreibvorgang auf diese Tabelle, ob relevant oder nicht.
- Ein ORM oder Framework, das stillschweigend die Isolationsebene setzt — Teams entdecken manchmal erst, dass sie Repeatable Read fahren, wenn dieser Fehler unter Last auftaucht.
So diagnostizieren Sie ihn
- Bestätigen Sie, welche Ebene Sie tatsächlich fahren:
SHOW default_transaction_isolation;und prüfen Sie, ob die Anwendung sie pro Transaktion setzt. - Protokollieren und zählen Sie das Auftreten von SQLSTATE
40001: eine niedrige Hintergrundrate unter Serializable ist normal und gesund; ein an einen Endpunkt gebundener Anstieg identifiziert die kollidierende Arbeitslast. - Identifizieren Sie die beiden Seiten des Konflikts aus den Anwendungslogs (welche Transaktionen dieselben Zeilen oder Tabellen um den Fehlerzeitpunkt herum anfassen). Die
DETAIL-Reason-Codes des Fehlers sind meist nützlich, um zu bestätigen, dass es SSI am Werk ist, nicht um auf den Gegenüber zu zeigen.
So beheben Sie ihn
- Wiederholen Sie die gesamte Transaktion bei SQLSTATE
40001— einschließlich ihrer Lesevorgänge, denn die beim ersten Mal gelesenen Werte sind genau das, was sich geändert haben kann. Begrenzte Versuche, kleiner Backoff, keine Seiteneffekte (E-Mails, HTTP-Aufrufe) innerhalb des wiederholten Blocks. - Halten Sie Transaktionen kurz und fassen Sie so wenige Zeilen wie möglich an.
- Bei Serializable lassen Sie Lesevorgänge Indizes nutzen: präzise Index-Scans nehmen enge Prädikat-Locks und kollidieren weit seltener als sequenzielle Scans.
- Wenn ein Hotspot ständige Wiederholungen erzeugt, erwägen Sie, diesen einen Pfad stattdessen mit Read Committed plus explizitem Locking (
SELECT ... FOR UPDATE) zu behandeln — die Contention löst sich durch Warten statt durch Abbruch.
🔍
Weiterführend: Transaktions-Isolationsebenen in der Praxis — was jede Ebene garantiert, Write Skew und ein ganzer Abschnitt zum korrekten Wiederholen.
