MySQL
SELECTEER ... VOOR UPDATE met UPDATE
Transacties gebruiken met InnoDB (auto-commit uitgeschakeld), een SELECT ... FOR UPDATE
staat één sessie toe om een bepaald record (of records) tijdelijk te vergrendelen, zodat geen enkele andere sessie het kan bijwerken. Vervolgens kan de sessie binnen dezelfde transactie daadwerkelijk een UPDATE
. uitvoeren op hetzelfde record en de transactie vastleggen of terugdraaien. Dit zou u in staat stellen om het record te vergrendelen, zodat geen enkele andere sessie het kan bijwerken terwijl u misschien een andere zakelijke logica doet.
Dit wordt bereikt met vergrendeling. InnoDB gebruikt indexen voor het vergrendelen van records, dus het vergrendelen van een bestaand record lijkt eenvoudig:vergrendel gewoon de index voor dat record.
SELECTEER ... VOOR UPDATE met INSERT
Om echter SELECT ... FOR UPDATE
. te gebruiken met INSERT
, hoe vergrendel je een index voor een record dat nog niet bestaat? Als u het standaard isolatieniveau van REPEATABLE READ
gebruikt , InnoDB zal ook gebruik maken van gap sloten. Zolang je de id
. weet (of zelfs een reeks id's) om te vergrendelen, dan kan InnoDB de opening vergrendelen zodat er geen ander record in die opening kan worden ingevoegd totdat we er klaar mee zijn.
Als uw id
kolom een auto-increment kolom was, dan SELECT ... FOR UPDATE
met INSERT INTO
zou problematisch zijn omdat je niet zou weten wat de nieuwe id
was totdat u het invoegde. Aangezien u echter de id
die u wilt invoegen, SELECT ... FOR UPDATE
met INSERT
zal werken.
WAARSCHUWING
Op het standaard isolatieniveau, SELECT ... FOR UPDATE
op een niet-bestaand record doet niet andere transacties blokkeren. Dus, als twee transacties beide een SELECT ... FOR UPDATE
op hetzelfde niet-bestaande indexrecord krijgen ze allebei de vergrendeling, en geen van beide transacties kan het record bijwerken. Als ze het proberen, wordt er zelfs een impasse gedetecteerd.
Daarom, als u niet met een impasse wilt omgaan, kunt u het volgende doen:
INVOEREN IN ...
Start een transactie en voer de INSERT
. uit . Voer uw bedrijfslogica uit en voer de transactie door of maak een rollback. Zodra u de INSERT
op de niet-bestaande recordindex bij de eerste transactie, worden alle andere transacties geblokkeerd als ze proberen INSERT
een record met dezelfde unieke index. Als de tweede transactie probeert een record met dezelfde index in te voegen nadat de eerste transactie de invoeging heeft uitgevoerd, krijgt deze een "duplicaatsleutel"-fout. Handel dienovereenkomstig.
SELECTEER ... VERGRENDELEN IN DE DELENMODUS
Als u selecteert met LOCK IN SHARE MODE
voor de INSERT
, als een eerdere transactie dat record heeft ingevoegd maar nog niet is vastgelegd, de SELECT ... LOCK IN SHARE MODE
wordt geblokkeerd totdat de vorige transactie is voltooid.
Dus om de kans op dubbele sleutelfouten te verkleinen, vooral als u de vergrendelingen een tijdje vasthoudt terwijl u bedrijfslogica uitvoert voordat u ze vastlegt of terugdraait:
SELECT bar FROM FooBar WHERE foo = ? LOCK FOR UPDATE
- Als er geen records zijn geretourneerd,
INSERT INTO FooBar (foo, bar) VALUES (?, ?)