Oracle
 sql >> Datenbank >  >> RDS >> Oracle

Verhindert SELECT FOR UPDATE das Einfügen anderer Verbindungen, wenn die Zeile nicht vorhanden ist?

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:

  1. SELECT bar FROM FooBar WHERE foo = ? LOCK FOR UPDATE
  2. Wenn keine Datensätze zurückgegeben werden, dann
  3. INSERT INTO FooBar (foo, bar) VALUES (?, ?)