MySQL
WÄHLEN ... FÜR UPDATE mit UPDATE
Bei Transaktionen mit InnoDB (Auto-Commit ausgeschaltet), ein SELECT ... FOR UPDATE
ermöglicht es einer Sitzung, einen bestimmten Datensatz (oder Datensätze) vorübergehend zu sperren, sodass keine andere Sitzung ihn aktualisieren kann. Dann kann die Sitzung innerhalb derselben Transaktion tatsächlich ein UPDATE
durchführen auf demselben Datensatz und führen Sie die Transaktion durch oder machen Sie sie rückgängig. Dies würde es Ihnen ermöglichen, den Datensatz zu sperren, sodass keine andere Sitzung ihn aktualisieren könnte, während Sie vielleicht eine andere Geschäftslogik ausführen.
Dies wird durch Verriegelung erreicht. InnoDB verwendet Indizes zum Sperren von Datensätzen, daher scheint das Sperren eines vorhandenen Datensatzes einfach zu sein – sperren Sie einfach den Index für diesen Datensatz.
WÄHLEN ... FÜR UPDATE mit EINFÜGEN
Um jedoch SELECT ... FOR UPDATE
zu verwenden mit INSERT
, wie sperren Sie einen Index für einen Datensatz, der noch nicht existiert? Wenn Sie die Standardisolationsstufe von REPEATABLE READ
verwenden , wird InnoDB auch gap verwenden Schlösser. Solange Sie die id
kennen (oder sogar einen Bereich von IDs) zu sperren, dann kann InnoDB die Lücke sperren, sodass kein weiterer Datensatz in diese Lücke eingefügt werden kann, bis wir damit fertig sind.
Wenn Ihre id
Spalte eine Auto-Increment-Spalte waren, dann SELECT ... FOR UPDATE
mit INSERT INTO
wäre problematisch, weil Sie nicht wissen würden, was die neue id
ist war, bis Sie es eingefügt haben. Da Sie jedoch die id
kennen die Sie einfügen möchten, SELECT ... FOR UPDATE
mit INSERT
wird funktionieren.
ACHTUNG
Auf der Standardisolationsebene SELECT ... FOR UPDATE
auf einem nicht existierenden Datensatz nicht andere Transaktionen blockieren. Wenn also zwei Transaktionen beide ein SELECT ... FOR UPDATE
ausführen für denselben nicht vorhandenen Indexdatensatz erhalten beide die Sperre, und keine Transaktion kann den Datensatz aktualisieren. Wenn sie es versuchen, wird sogar ein Deadlock erkannt.
Wenn Sie sich also nicht mit einem Deadlock befassen möchten, können Sie einfach Folgendes tun:
EINFÜGEN IN ...
Starten Sie eine Transaktion und führen Sie INSERT
aus . Führen Sie Ihre Geschäftslogik aus und führen Sie die Transaktion entweder fest oder setzen Sie sie zurück. Sobald Sie INSERT
ausführen auf dem nicht vorhandenen Datensatzindex bei der ersten Transaktion werden alle anderen Transaktionen blockiert, wenn sie versuchen, INSERT
auszuführen ein Datensatz mit demselben eindeutigen Index. Wenn die zweite Transaktion versucht, einen Datensatz mit demselben Index einzufügen, nachdem die erste Transaktion die Einfügung festgeschrieben hat, erhält sie einen „Duplicate Key“-Fehler. Gehen Sie entsprechend vor.
AUSWÄHLEN ... IM TEILEN-MODUS SPERREN
Wenn Sie mit LOCK IN SHARE MODE
auswählen vor dem INSERT
, wenn eine vorherige Transaktion diesen Datensatz eingefügt, aber noch nicht festgeschrieben hat, der SELECT ... LOCK IN SHARE MODE
blockiert, bis die vorherige Transaktion abgeschlossen ist.
Um also die Wahrscheinlichkeit doppelter Schlüsselfehler zu verringern, insbesondere wenn Sie die Sperren für eine Weile halten, während Sie die Geschäftslogik ausführen, bevor Sie sie festschreiben oder zurücksetzen:
SELECT bar FROM FooBar WHERE foo = ? LOCK FOR UPDATE
- Wenn keine Datensätze zurückgegeben werden, dann
INSERT INTO FooBar (foo, bar) VALUES (?, ?)