Wann sollte SELECT… FOR UPDATE verwendet werden?

118

Bitte helfen Sie mir, den Anwendungsfall dahinter zu verstehen SELECT ... FOR UPDATE.

Frage 1 : Ist das Folgende ein gutes Beispiel dafür, wann SELECT ... FOR UPDATEes verwendet werden sollte?

Gegeben:

  • Zimmer [id]
  • Tags [ID, Name]
  • room_tags [room_id, tag_id]
    • room_id und tag_id sind Fremdschlüssel

Die Anwendung möchte alle Räume und ihre Tags auflisten, muss jedoch zwischen Räumen ohne Tags und Räumen unterscheiden, die entfernt wurden. Wenn SELECT ... FOR UPDATE nicht verwendet wird, kann Folgendes passieren:

  • Anfänglich:
    • Zimmer enthält [id = 1]
    • Tags enthält [id = 1, name = 'cats']
    • room_tags enthält [room_id = 1, tag_id = 1]
  • Thread 1: SELECT id FROM rooms;
    • returns [id = 1]
  • Thread 2: DELETE FROM room_tags WHERE room_id = 1;
  • Thread 2: DELETE FROM rooms WHERE id = 1;
  • Thread 2: [schreibt die Transaktion fest]
  • Thread 1: SELECT tags.name FROM room_tags, tags WHERE room_tags.tag_id = 1 AND tags.id = room_tags.tag_id;
    • gibt eine leere Liste zurück

Jetzt denkt Thread 1, dass Raum 1 keine Tags hat, aber in Wirklichkeit wurde der Raum entfernt. Um dieses Problem zu lösen, sollte Thread 1 verwendet werden SELECT id FROM rooms FOR UPDATE, wodurch verhindert wird, dass Thread 2 gelöscht wird, roomsbis Thread 1 fertig ist. Ist das korrekt?

Frage 2 : Wann sollte man verwenden SERIALIZABLETransaktion Isolation gegenüber READ_COMMITTEDmit SELECT ... FOR UPDATE?

Es wird erwartet, dass die Antworten portabel sind (nicht datenbankspezifisch). Wenn dies nicht möglich ist, erklären Sie bitte warum.

Gili
quelle
2
Welches RDBMS verwenden Sie?
Quassnoi
2
@Quassnoi, wie am Ende der Frage erwähnt, suche ich nach einer tragbaren (nicht datenbankspezifischen) Lösung.
Gili
2
Sind die Optionen REPEATABLE_READund READ_COMMITTEDsogar tragbare Optionen? Die einzigen Ergebnisse, die ich für diese bekomme, sind für MSSQL Server
Billy ONeal
3
@BillyONeal: Beachten Sie, dass Isolationsmodi garantieren, dass Sie keine Macken sehen, die sie nicht zulassen, aber nichts über die Macken sagen, die sie zulassen. Dies bedeutet, dass durch das Einstellen des READ COMMITTEDModus beispielsweise nicht definiert wird, ob tatsächlich Datensätze angezeigt werden, die von einer anderen Transaktion festgeschrieben wurden. Es wird lediglich sichergestellt, dass niemals nicht festgeschriebene Datensätze angezeigt werden.
Quassnoi
3
A select ... for updateauf roomserlauben noch room_tagsgelöscht werden , weil sie getrennte Tabellen sind. Wollten Sie fragen, ob die for updateKlausel Löschungen verhindert rooms?
Chris Saxon

Antworten:

83

Die einzige tragbare Möglichkeit, die Konsistenz zwischen Räumen und Tags zu erreichen und sicherzustellen, dass Räume nach dem Löschen nie zurückgegeben werden, besteht darin, sie zu sperren SELECT FOR UPDATE.

In einigen Systemen ist das Sperren jedoch ein Nebeneffekt der Parallelitätskontrolle, und Sie erzielen dieselben Ergebnisse, ohne dies FOR UPDATEexplizit anzugeben .


Um dieses Problem zu lösen, sollte Thread 1 verwendet werden SELECT id FROM rooms FOR UPDATE, wodurch verhindert wird, dass Thread 2 gelöscht wird, roomsbis Thread 1 fertig ist. Ist das korrekt?

Dies hängt von der Parallelitätskontrolle ab, die Ihr Datenbanksystem verwendet.

  • MyISAMin MySQL(und mehreren anderen alten Systemen) sperrt die gesamte Tabelle für die Dauer einer Abfrage.

  • In SQL Server, SELECTAbfragen Ort eines Shared - Sperren auf den Aufzeichnungen / pages / Tabellen sie untersucht haben, während DMLAbfragen statt Update - Sperren (die später zu gemeinsamen Sperren , um exklusiven oder degradiert befördert zu werden ). Exklusive Sperren sind nicht mit gemeinsam genutzten Sperren kompatibel. Daher wird entweder SELECToder die DELETEAbfrage gesperrt, bis eine andere Sitzung festgeschrieben wird.

  • In Datenbanken , die Verwendung MVCC(wie Oracle, PostgreSQL, MySQLmit InnoDB), eine DMLerstellten Abfrage eine Kopie des Datensatz (in der einen oder anderen Art und Weise) und in der Regel kehrt nicht blockieren Schriftsteller und vice Leser. Für diese Datenbanken SELECT FOR UPDATEwäre a praktisch: Es würde entweder SELECToder die DELETEAbfrage sperren, bis eine andere Sitzung festgeschrieben wird, genau wie dies der SQL ServerFall ist.

Wann sollte man verwenden REPEATABLE_READTransaktion Isolation gegenüber READ_COMMITTEDmit SELECT ... FOR UPDATE?

REPEATABLE READVerbietet im Allgemeinen keine Phantomzeilen (Zeilen, die in einer anderen Transaktion angezeigt oder verschwunden sind, anstatt geändert zu werden)

  • In Oracleund früheren PostgreSQLVersionen REPEATABLE READist eigentlich ein Synonym für SERIALIZABLE. Grundsätzlich bedeutet dies, dass die Transaktion nach dem Start keine Änderungen mehr sieht. In diesem Setup gibt die letzte Thread 1Abfrage den Raum zurück, als wäre er nie gelöscht worden (was möglicherweise das ist, was Sie wollten oder nicht). Wenn Sie die Räume nach dem Löschen nicht mehr anzeigen möchten, sollten Sie die Zeilen mit sperrenSELECT FOR UPDATE

  • In InnoDB, REPEATABLE READund SERIALIZABLEsind verschiedene Dinge: Leser - SERIALIZABLEModus Set Next-Key - Sperre für die Aufzeichnungen sie bewerten, effektiv die gleichzeitige Verhinderung DMLauf sich. Sie benötigen also keinen SELECT FOR UPDATEim serialisierbaren Modus, sondern benötigen ihn in REPEATABLE READoder READ COMMITED.

Beachten Sie, dass der Standard für Isolationsmodi vorschreibt, dass Sie bestimmte Macken in Ihren Abfragen nicht sehen, aber nicht definieren, wie (mit Sperren oder mit MVCCoder auf andere Weise).

Wenn ich "Sie brauchen nicht SELECT FOR UPDATE" sage, hätte ich wirklich "wegen der Nebenwirkungen bestimmter Datenbank-Engine-Implementierungen" hinzufügen sollen.

Quassnoi
quelle
1
Der letzte Punkt ist der Kern der Sache, denke ich: "Sie brauchen kein SELECT FOR UPDATE im serialisierbaren Modus, aber Sie brauchen sie in REPEATABLE READ oder READ COMMITED."
Colin 't Hart
Du hast recht. Die zweite Frage sollte gestellt haben, wann SERIALIZABLEim Vergleich zu READ_COMMITTEDmit verwendet werden sollte SELECT ... FOR UPDATE. Können Sie bitte Ihre Antwort aktualisieren, um diese aktualisierte Frage widerzuspiegeln?
Gili
1
@Gili: "Du brauchst keinen SELECT FOR UPDATEim serialisierbaren Modus", mit InnoDB. Bei den anderen MVCCSystemen sind die beiden Synonyme und Sie benötigen SELECT FOR UPDATE.
Quassnoi
1
Ich finde, Colins Beitrag beantwortet meine spezifischen Fragen besser als Ihre Antwort, aber ich schätze alle Referenzen, die Sie angegeben haben. Ich werde eine Antwort akzeptieren, die die beiden am besten kombiniert (spezifische Antworten oben, unterstützende Referenzen unten).
Gili
This depends on the concurrency control your database system is using: Ich denke du spaltest Haare. Alle Fälle, die Sie unten auflisten, besagen, dass der Raum zwischen SELECTdem Ende der Transaktion nicht gelöscht wird. Sollte die Antwort nicht einfach Yesmit den folgenden unterstützenden Referenzen sein?
Gili
33

Kurze Antworten:

Q1: Ja.

F2: Egal welche Sie verwenden.

Lange Antwort:

A select ... for updatewird (wie impliziert) bestimmte Zeilen auswählen, sie aber auch sperren, als ob sie bereits von der aktuellen Transaktion aktualisiert worden wären (oder als ob die Identitätsaktualisierung durchgeführt worden wäre). Auf diese Weise können Sie sie in der aktuellen Transaktion erneut aktualisieren und dann festschreiben, ohne dass eine andere Transaktion diese Zeilen in irgendeiner Weise ändern kann.

Eine andere Sichtweise ist, als ob die folgenden zwei Anweisungen atomar ausgeführt werden:

select * from my_table where my_condition;

update my_table set my_column = my_column where my_condition;

Da die von betroffenen Zeilen my_conditiongesperrt sind, kann keine andere Transaktion sie in irgendeiner Weise ändern, und daher macht die Transaktionsisolationsstufe hier keinen Unterschied.

Beachten Sie auch, dass die Transaktionsisolationsstufe unabhängig vom Sperren ist: Wenn Sie eine andere Isolationsstufe festlegen, können Sie das Sperren und Aktualisieren von Zeilen in einer anderen Transaktion, die von Ihrer Transaktion gesperrt werden, nicht umgehen.

Was Transaktionsisolationsstufen (auf verschiedenen Ebenen) garantieren, ist die Konsistenz der Daten während laufender Transaktionen.

Colin 't Hart
quelle
1
Ich denke What transaction isolation levels do guarantee [...] is the consistency of data once transactions are completed.fälschlicherweise impliziert dies, dass Isolationsstufen keinen Einfluss darauf haben, was während einer Transaktion passiert . Ich empfehle, diesen Abschnitt zu überarbeiten und detailliertere Informationen darüber zu liefern, wie sie sich auf das auswirken, was Sie während einer Transaktion sehen (oder nicht sehen).
Gili
1
Ich finde, dass Ihr Beitrag meine spezifischen Fragen besser beantwortet als der von Quassnoi, aber ich schätze alle Referenzen, die er zur Verfügung gestellt hat. Ich werde eine Antwort akzeptieren, die die beiden am besten kombiniert (spezifische Antworten oben, unterstützende Referenzen unten).
Gili
Verriegelung und Isolierung sind austauschbar kompliziert. Gibt es also Bücher, um das Wissen darüber zu erlangen?
Chao