MySQL
VYBERTE... PRO AKTUALIZACI pomocí AKTUALIZACE
Pomocí transakcí s InnoDB (automatické potvrzení vypnuto) a SELECT ... FOR UPDATE
umožňuje jedné relaci dočasně uzamknout určitý záznam (nebo záznamy), aby jej žádná jiná relace nemohla aktualizovat. Potom v rámci stejné transakce může relace skutečně provést UPDATE
na stejném záznamu a transakci potvrdit nebo vrátit zpět. To by vám umožnilo uzamknout záznam, aby jej žádná jiná relace nemohla aktualizovat, zatímco vy možná budete dělat nějakou jinou obchodní logiku.
Toho se dosáhne zamykáním. InnoDB využívá indexy pro zamykání záznamů, takže uzamčení existujícího záznamu se zdá snadné – jednoduše uzamkněte index pro daný záznam.
VLOŽTE... PRO AKTUALIZACI pomocí INSERT
Chcete-li však použít SELECT ... FOR UPDATE
pomocí INSERT
, jak zamknete index pro záznam, který ještě neexistuje? Pokud používáte výchozí úroveň izolace REPEATABLE READ
, InnoDB bude také využívat mezera zámky. Pokud znáte id
(nebo dokonce rozsah id) k uzamčení, pak InnoDB může uzamknout mezeru, takže do této mezery nelze vložit žádný další záznam, dokud s tím neskončíme.
Pokud vaše id
sloupec byl sloupec s automatickým přírůstkem, pak SELECT ... FOR UPDATE
pomocí INSERT INTO
by bylo problematické, protože byste nevěděli, co je nové id
byl, dokud jste jej nevložili. Protože však znáte id
které si přejete vložit, SELECT ... FOR UPDATE
pomocí INSERT
bude fungovat.
UPOZORNĚNÍ
Na výchozí úrovni izolace SELECT ... FOR UPDATE
na neexistujícím záznamu ne blokovat další transakce. Pokud tedy dvě transakce provedou obě SELECT ... FOR UPDATE
na stejném neexistujícím záznamu indexu, oba získají zámek a žádná transakce nebude moci záznam aktualizovat. Ve skutečnosti, pokud se o to pokusí, bude detekováno uváznutí.
Pokud se tedy nechcete vypořádat se zablokováním, můžete udělat následující:
INSERT DO...
Zahajte transakci a proveďte INSERT
. Proveďte svou obchodní logiku a transakci buď potvrďte, nebo vraťte zpět. Jakmile provedete INSERT
na neexistujícím indexu záznamu při první transakci se všechny ostatní transakce zablokují, pokud se pokusí INSERT
záznam se stejným jedinečným indexem. Pokud se druhá transakce pokusí vložit záznam se stejným indexem poté, co první transakce potvrdí vložení, zobrazí se chyba "duplicitní klíč". Zacházejte podle toho.
VYBRAT... ZAMKNOUT V REŽIMU SDÍLENÍ
Pokud vyberete pomocí LOCK IN SHARE MODE
před INSERT
, pokud předchozí transakce vložila tento záznam, ale ještě nebyla potvrzena, SELECT ... LOCK IN SHARE MODE
se zablokuje, dokud nebude dokončena předchozí transakce.
Chcete-li tedy snížit možnost duplicitních chyb klíčů, zejména pokud zámky na chvíli podržíte při provádění obchodní logiky, než je provedete nebo je vrátíte zpět:
SELECT bar FROM FooBar WHERE foo = ? LOCK FOR UPDATE
- Pokud se nevrátily žádné záznamy, pak
INSERT INTO FooBar (foo, bar) VALUES (?, ?)