Postgres, MVCC und Locking

8

Ich habe eine Reihe von SQL-Anweisungen, die wie folgt aussehen:

BEGIN;
SELECT counter FROM table WHERE id=X FOR UPDATE;
REALLY COMPLEX QUERY;
UPDATE table SET counter=Y WHERE id=X;
END;

Ich möchte verhindern, dass der Zähler gelesen wird, während ich seinen Wert neu berechne, aber laut den Postgres-Dokumenten "Sperren auf Zeilenebene wirken sich nicht auf die Datenabfrage aus; sie blockieren nur Schreiber in derselben Zeile."

Fragen:

  1. Was bringt eine "exklusive" Zeilensperre, wenn sie das Lesen nicht verhindert? Ist es nur zu verhindern, dass andere Transaktionen Freigabesperren übernehmen?
  2. Wenn ich die Zeile mit SELECT ... FOR SHARE lese, hat dies den gleichen Effekt wie eine "exklusive" Sperre?
  3. Ist es möglich, MVCC für eine Tabelle / ein Schema / eine Datenbank zu deaktivieren und direkte Schreibvorgänge zuzulassen?
Kevin
quelle

Antworten:

5

zu 1) Jede andere Sitzung liest die von Ihrer Transaktion geänderten Daten wie vor Ihrer "BEGIN" -Anweisung, solange Ihre Transaktion nicht festgeschrieben wurde. Sobald Ihre Transaktion festgeschrieben wurde, wird der neue Wert des Zählers gelesen. Der Punkt ist, dass andere nicht warten müssen und immer eine konsistente Datenbank sehen.

zu 2), 3) Warum versuchst du es nicht mit "ACCESS EXCLUSIVE"? (siehe http://www.postgresql.org/docs/current/static/explicit-locking.html )

BEARBEITEN: Wenn Sie die gesamte Tabelle nicht mit einer "ACCESS EXCLUSIVE" -Sperre sperren möchten, können Sie auch eine "Advisory Lock" verwenden (siehe Abschnitt 13.3.4 im obigen Link).

jp
quelle
1

Wenn die zu blockierenden Lesevorgänge gleichzeitige Ausführungen derselben Transaktion nur mit unterschiedlichen Daten sind, verwenden Sie eine UPDATE-Anweisung mit Rückgabeklausel.

Überprüfen Sie die Dokumente zur UPDATE-Anweisung. Um Ihre Frage zum Punkt einer exklusiven Zeilensperre zu beantworten, müssen Sie verhindern, dass Dateninkonsistenzen gleichzeitig aktualisiert werden. Die Leser erhalten jederzeit eine konsistente Ansicht der Datenbank.

Ketema
quelle
1

In dem von Ihnen angegebenen Codebeispiel wird bei allen Lesevorgängen dieser Zeile der alte Wert angezeigt, bis diese Transaktion festgeschrieben wird.

1) Betrachten Sie das Codebeispiel, das zweimal gleichzeitig mit demselben Wert von ausgeführt wird X. Wenn Instanz A ausgeführt wurde select ... for update, hat sie diese Zeile gesperrt, bis sie festgeschrieben wird. Instanz B blockiert auf diese Weise den Versuch, die Sperre auf die gleiche Weise zu aktivieren. Nur wenn A festschreibt, kann B fortfahren. B erhält dann den Wert A, der in seiner letzten Aktualisierung zurückgelassen wurde.

Wenn dies Ydavon abhängt, welchen Wert die select ... for updateAbfrage oder einen anderen Lesevorgang im 'komplexen' Teil gelesen hat, hat dies den gleichen Effekt, als ob sie seriell ausgeführt würden - Sie erhalten nicht die Race-Bedingung, unter der eines der Ergebnisse angezeigt wird verworfen.

Wenn dies Ynur das Ergebnis der komplexen Abfragen in der Mitte ist, wird es parallel ausgeführt, ohne select ... for updatedass beide das gleiche Update durchführen.

2) for shareermöglicht es anderen Auswahlen in dieser Zeile, fortzufahren, bewirkt jedoch, dass alles andere versucht select ... for updateoder update ...blockiert wird, bis dies erledigt ist. Wenn der Beispielcode zweimal ausgeführt wurde, gleichzeitig würden sie Deadlock die bei Erreichen updateAussage, so dass einer von ihnen abgebrochen werden.

3) Nein. Dies zu tun ist gefährlich, da ein Leser ein halb aktualisiertes Feld lesen könnte. (Oder lesen Sie den Rest eines Feldes, das nach dem Beginn des Lesens aktualisiert wurde.) Es kann auch die Konsistenz beeinträchtigen und dem Leser einen Wert anzeigen, der nach dem Start seiner Transaktion aktualisiert wurde.

MaHuJa
quelle