MySQL
VÄLJ ... FÖR UPPDATERING med UPPDATERING
Genom att använda transaktioner med InnoDB (auto-commit avstängd), en SELECT ... FOR UPDATE
tillåter en session att tillfälligt låsa en viss post (eller poster) så att ingen annan session kan uppdatera den. Sedan, inom samma transaktion, kan sessionen faktiskt utföra en UPDATE
på samma post och begå eller återställa transaktionen. Detta skulle tillåta dig att låsa ned posten så att ingen annan session kan uppdatera den medan du kanske gör någon annan affärslogik.
Detta görs med låsning. InnoDB använder index för att låsa poster, så det verkar enkelt att låsa en befintlig post – lås helt enkelt indexet för den posten.
VÄLJ ... FÖR UPPDATERING med INSERT
Men för att använda SELECT ... FOR UPDATE
med INSERT
, hur låser man ett index för en post som inte finns ännu? Om du använder standardisoleringsnivån REPEATABLE READ
, kommer InnoDB också att använda gap lås. Så länge du känner till id
(eller till och med ett antal id) för att låsa, då kan InnoDB låsa gapet så att ingen annan post kan infogas i den luckan förrän vi är klara med den.
Om ditt id
kolumnen var en kolumn för automatisk ökning, sedan SELECT ... FOR UPDATE
med INSERT INTO
skulle vara problematiskt eftersom du inte skulle veta vad det nya id
är var tills du satte in den. Men eftersom du känner till id
som du vill infoga, SELECT ... FOR UPDATE
med INSERT
kommer att fungera.
VARNING
På standardisoleringsnivån, SELECT ... FOR UPDATE
på en icke-existerande post inte blockera andra transaktioner. Så om två transaktioner båda gör en SELECT ... FOR UPDATE
på samma obefintliga indexpost får de båda låset, och ingen av transaktionerna kommer att kunna uppdatera posten. Faktum är att om de försöker, kommer ett dödläge att upptäckas.
Därför, om du inte vill hantera ett dödläge, kan du bara göra följande:
INSERT I ...
Starta en transaktion och utför INSERT
. Gör din affärslogik och antingen begå eller återställa transaktionen. Så snart du gör INSERT
på det obefintliga postindexet på den första transaktionen, blockeras alla andra transaktioner om de försöker INSERT
en post med samma unika index. Om den andra transaktionen försöker infoga en post med samma index efter att den första transaktionen genomfört infogningen, kommer den att få ett "duplicerat nyckel"-fel. Hantera därefter.
VÄLJ ... LÅS I DELNINGSLÄGE
Om du väljer med LOCK IN SHARE MODE
före INSERT
, om en tidigare transaktion har infogat den posten men inte har genomförts ännu, SELECT ... LOCK IN SHARE MODE
blockeras tills den föregående transaktionen har slutförts.
Så för att minska risken för duplicerade nyckelfel, särskilt om du håller låsen ett tag medan du utför affärslogik innan du begår dem eller rullar tillbaka dem:
SELECT bar FROM FooBar WHERE foo = ? LOCK FOR UPDATE
- Om inga poster returneras, då
INSERT INTO FooBar (foo, bar) VALUES (?, ?)