In manchen Fällen ist eine konsistente Leseoperation nicht das
Richtige. Vielleicht möchten Sie ja eine neue Zeile in Ihre
child
-Tabelle einfügen und dabei
sicherstellen, dass das Kind in der Tabelle
parent
auch wirklich ein Elternteil hat. Das
folgende Beispiel zeigt, wie Sie referenzielle Integrität in
Ihren Anwendungscode implementieren können.
Angenommen, Sie verwenden eine konsistente Leseoperation, um die
Tabelle parent
zu lesen, und sehen auch
tatsächlich die Elternzeile des Kindes in der Tabelle. Können
Sie die Kindzeile nun auch beruhigt in die Tabelle
child
einfügen? Nein, denn es kann ja sein,
dass irgendein anderer Benutzer zwischenzeitlich die Elternzeile
aus der Tabelle parent
gelöscht hat, ohne
dass Sie es merkten.
Die Lösung: Sie führen das SELECT
in dem
Sperrmodus LOCK IN SHARE MODE
aus:
SELECT * FROM parent WHERE NAME = 'Jones' LOCK IN SHARE MODE;
Wird eine Leseoperation im Share-Modus ausgeführt, so liest sie
die aktuellsten verfügbaren Daten und errichtet eine
Shared-Sperre auf den Zeilen, die sie liest. Diese verhindert,
dass andere Benutzer die Zeile ändern oder löschen. Wenn die
Daten zu einer noch unvollendeten Transaktion einer anderen
Clientverbindung gehören, warten wir, bis die Transaktion
committet ist. wenn wir gesehen haben, dass die obige Anfrage
den Parent 'Jones'
zurückgibt, können wir
unseren Eintrag beruhigt in die child
-Tabelle
einfügen und unsere Transaktion committen.
Betrachten wir ein anderes Beispiel: Wir haben in der Tabelle
child_codes
ein Zählerfeld eines
Integer-Typs, das wir dazu benutzen, jedem Kind, das der
child
-Tabelle hinzugefügt wird, eine
eindeutige Kennnummer zu geben. Da wäre es natürlich keine
gute Idee, den Wert des Zählers mit einer konsistenten
Leseoperation oder im Shared-Modus zu lesen, da zwei
Datenbanknutzer dann vielleicht denselben Zählerwert sehen und
einen Fehler wegen Schlüsselduplikaten auslösen, sofern sie
versuchen, Kindeinträge mit derselben Nummer in die Tabelle
einzufügen.
Hier ist LOCK IN SHARE MODE
keine gute
Lösung. Denn wenn zwei Benutzer gleichzeitig den Zähler lesen,
könnte mindestens einer von ihnen in einen Deadlock geraten,
wenn er versucht, den Zähler zu aktualisieren.
Hier haben Sie zwei gute Möglichkeiten, das Lesen und
Inkrementieren des Zählers zu implementieren: (1) Sie
inkrementierten zuerst den Zähler um 1 und führen erst dann
die Leseoperation durch, oder (2) Sie lesen den Zähler zuerst
im Sperrmodus FOR UPDATE
und inkrementieren
ihn danach. Der zweite Ansatz kann folgendermaßen implementiert
werden:
SELECT counter_field FROM child_codes FOR UPDATE; UPDATE child_codes SET counter_field = counter_field + 1;
Ein SELECT … FOR UPDATE
liest die
neuesten verfügbaren Daten und errichtet eine exklusive Sperre
auf jeder Zeile, die es liest. Somit setzt es dieselben Sperren,
die auch ein Searched SQL UPDATE
auf den
Zeilen erwerben würde.
Die obige Beschreibung ist nur ein Beispiel dafür, wie
SELECT … FOR UPDATE
funktioniert. In
MySQL können Sie einen eindeutigen Identifier grundsätzlich
mit nur einem einzigen Tabellenzugriff generieren:
UPDATE child_codes SET counter_field = LAST_INSERT_ID(counter_field + 1); SELECT LAST_INSERT_ID();
Die SELECT
-Anweisung ruft nur die
Identifier-Information ab (die für die aktuelle Verbindung
spezifisch ist). Sie greift auf keine Tabellen zu.
Sperren von IN SHARE MODE
- und FOR
UPDATE
-Leseoperationen werden freigegeben, wenn die
Transaktion committet oder zurückgerollt wird.
Dies ist eine Übersetzung des MySQL-Referenzhandbuchs, das sich auf dev.mysql.com befindet. Das ursprüngliche Referenzhandbuch ist auf Englisch, und diese Übersetzung ist nicht notwendigerweise so aktuell wie die englische Ausgabe. Das vorliegende deutschsprachige Handbuch behandelt MySQL bis zur Version 5.1.