2012-04-20 11 views
19

Ich bin ein wenig verwirrt über PostgreSQL Deadlocks lesen.Deadlocks in PostgreSQL beim Ausführen von UPDATE

Ein typisches Beispiel ist Deadlock:

-- Transaction 1 
UPDATE customer SET ... WHERE id = 1 
UPDATE customer SET ... WHERE id = 2 

-- Transaction 2 
UPDATE customer SET ... WHERE id = 2 
UPDATE customer SET ... WHERE id = 1 

Aber was ist, wenn ich den Code wie folgt ändern:

-- Transaction 1 
UPDATE customer SET ... WHERE id IN (1, 2) 

-- Transaction 2 
UPDATE customer SET ... WHERE id IN (1, 2) 

hier eine Möglichkeit der Deadlock sein?

Im Wesentlichen meine Frage ist: im zweiten Fall sperren PostgreSQL Zeilen eins nach dem anderen, oder sperren Sie den gesamten Bereich von der WHERE Bedingung abgedeckt?

Vielen Dank im Voraus!

Antwort

28

In PostgreSQL die Zeilen gesperrt werden, wie sie aktualisiert werden - in der Tat, die Art und Weise dies tatsächlich funktioniert, ist, dass jedes Tupel (Version einer Zeile) ein Systemfeld xmin hat aufgerufen, um anzuzeigen, welche Transaktion, dass Tupel Strom gemacht (durch Einfügen oder Aktualisieren) und ein Systemfeld namens xmax, um anzuzeigen, welche Transaktion das Tupel abgelaufen ist (durch Aktualisieren oder Löschen). Wenn Sie auf Daten zugreifen, überprüft es jedes Tupel, um festzustellen, ob es für Ihre Transaktion sichtbar ist, indem Sie Ihren aktiven "Snapshot" mit diesen Werten vergleichen.

Wenn Sie ein UPDATE ausführen und ein Tupel, das Ihren Suchbedingungen entspricht, ein xmin aufweist, das es für Ihren Snapshot und ein xmax einer aktiven Transaktion sichtbar macht, blockiert es und wartet darauf, dass diese Transaktion abgeschlossen wird. Wenn die Transaktion, die das Tupel zuerst aktualisiert hat, zurückrollt, wacht Ihre Transaktion auf und verarbeitet die Zeile. Wenn die erste Transaktion festgeschrieben wird, wird Ihre Transaktion abhängig von der aktuellen Isolationsstufe der Transaktion aktiviert.

Offensichtlich ist ein Deadlock das Ergebnis davon, dass Zeilen in anderer Reihenfolge auftreten. Es gibt keine Sperre auf Zeilenebene im RAM, die für alle Zeilen gleichzeitig verfügbar ist. Wenn jedoch Zeilen in der gleichen Reihenfolge aktualisiert werden, kann die Sperrung nicht aufgehoben werden. Leider garantiert die vorgeschlagene IN(1, 2) Syntax das nicht. Verschiedene Sitzungen können verschiedene Kostenfaktoren aktiv haben, eine Hintergrund- "Analyse" -Aufgabe kann die Statistik für die Tabelle zwischen der Erzeugung eines Plans und der anderen ändern, oder sie kann einen Seqscan verwenden und von der PostgreSQL-Optimierung beeinflusst werden, die einen neuen Seqscan verursacht einem bereits laufenden Prozess beizutreten und "umherzulaufen", um Festplatten-E/A zu reduzieren.

Wenn Sie die Aktualisierungen nacheinander in der gleichen Reihenfolge, im Anwendungscode oder mit einem Cursor ausführen, haben Sie nur einfache Blockierungen, keine Deadlocks. Im Allgemeinen sind relationale Datenbanken jedoch anfällig für Serialisierungsfehler, und es empfiehlt sich, auf sie über ein Framework zuzugreifen, das sie basierend auf SQLSTATE erkennt und automatisch die gesamte Transaktion von Anfang an erneut versucht. In PostgreSQL wird ein Serialisierungsfehler immer einen SQLSTATE von 40001 oder 40P01 haben.

http://www.postgresql.org/docs/current/interactive/mvcc-intro.html

+0

Vielen Dank! Also mein Beispiel oben kann einen Deadlock verursachen (weil wir die Reihenfolge nicht kennen, in der die Zeilen in beiden Transaktionen verarbeitet werden)? – vyakhir

+0

Es könnte einen Deadlock verursachen, obwohl das selten wäre; im Gegensatz zum ersten Beispiel (explizite Auswahl unterschiedlicher Ordnungen), wo es üblich wäre. Sie können Deadlocks ausschließen, indem Sie für die Dauer jeder Transaktion, die die Tabelle aktualisiert, eine Sperre auf Tabellenebene mit der entsprechenden Stärke vornehmen, aber diese Heilung ist möglicherweise schlimmer als die Krankheit. Weitere Informationen finden Sie in dem Abschnitt, auf den ich verwiesen habe. – kgrittn

+0

Aber veröffentlicht PostgreSQL die Sperre, nachdem die Zeile aktualisiert wurde, aber die gesamte UPDATE-Anweisung noch nicht beendet ist? Mit anderen Worten, wenn wir eine Aussage wie haben UPDATE ... WHERE ID IN (1,2,3,4,5) nach Postgresql Updates sagen, Zeile mit ID = 1 und fährt mit Zeile mit ID = 2, wird es die Zeile id = 1 freigeben? Wenn ja, wie wird es die Zeilen ggf. zurückrollen? – vyakhir

Verwandte Themen