Fehler im PostgreSQL-Sperrmechanismus oder Missverständnis des Mechanismus

8

Wir haben ein Problem mit dem Sperrmechanismus von PostgreSQL 9.0.12 festgestellt.

Dies ist unser minimaler Code, um das Problem zu reproduzieren:

Szenario

%Vor%

reproduzieren Code: Wenn Sie es in Ihrem PostgreSQL versuchen möchten - hier ist ein Code, den Sie kopieren / einfügen können.

Ich habe ein folgendes Datenbankschema:

%Vor%

öffne zwei psql shells:

in Shell 1:

%Vor%

in Shell 2:

%Vor%

Die zweite Aktualisierung von Äpfeln wird hängen bleiben und es scheint, dass der Prozess von Shell 2 auf die Transaktion von Shell 1 wartet, um zu beenden.

%Vor%

Haben wir etwas missverstanden oder ist es ein Bug in Postgres?

    
Amir Baron 24.03.2014, 09:59
quelle

1 Antwort

9

Es gibt keinen Fehler, und ich glaube nicht, dass Sie irgendetwas falsch verstehen; Du verpasst nur ein paar Teile des Puzzles.

Fremdschlüssel werden intern mit Sperren auf Zeilenebene implementiert. Ausgehend von Postgres 8.1 und bis 9.2 wird bei jedem Aktualisieren der referenzierenden Tabelle ( apples in diesem Fall) eine Abfrage ausgelöst, die SELECT FOR SHARE für die referenzierte Tabelle ( trees ) enthält. So blockiert SELECT FOR UPDATE in der ersten Transaktion die SELECT FOR SHARE der referentiellen Integrität für die zweite Transaktion. Dies verursacht den Block im zweiten Befehl.

Jetzt höre ich dich schreien: "Warte! Wie kommt es, dass es beim zweiten Befehl blockiert und nicht beim ersten? Die Erklärung ist einfach, wirklich - nur weil es eine einfache Optimierung gibt, die das interne SELECT FOR SHARE überspringt, wenn der Schlüssel nicht geändert wird. Dies ist jedoch zu einfach, wenn Sie ein Tupel ein zweites Mal aktualisieren, wird diese Optimierung nicht ausgelöst, da es schwieriger ist, die ursprünglichen Werte zu finden. Daher die Blockade.

Sie fragen sich vielleicht auch, warum ich gesagt habe, dass dies bis 9.2 ist - was ist mit 9.3? Der Hauptunterschied besteht darin, dass in 9.3 SELECT FOR KEY SHARE verwendet wird, was eine neue, leichtere Sperrstufe ist; es ermöglicht eine bessere Parallelität. Wenn du dein Beispiel in 9.3 probierst und auch SELECT FOR UPDATE in SELECT FOR NO KEY UPDATE änderst (was ein leichterer Modus als SELECT FOR UPDATE ist, der besagt, dass du das Tupel vielleicht aktualisieren wirst, aber du versprichst, den Primärschlüssel und das Versprechen nicht zu ändern um es nicht zu löschen), sollten Sie sehen, dass es nicht blockiert. (Sie können auch ein UPDATE für die referenzierte Zeile ausführen. Wenn Sie den Primärschlüssel nicht ändern, wird er auch nicht blockiert.)

Dieses 9.3-Zeug wurde von einem Patch von mir als Ссылка und ich denke, es war ein ziemlich cooler Hack (Die Commit-Nachricht hat einige weitere Details, wenn Sie sich für solche Sachen interessieren). Aber Vorsicht, benutze keine Versionen vor 9.3.4, denn dieser Patch war so komplex, dass ein paar schwerwiegende Fehler unbemerkt blieben und wir erst kürzlich behoben haben.

    
alvherre 24.03.2014, 13:50
quelle