MySQL
VÆLG ... TIL OPDATERING med OPDATERING
Ved at bruge transaktioner med InnoDB (auto-commit slået fra), en SELECT ... FOR UPDATE
tillader en session midlertidigt at låse en bestemt post (eller poster), så ingen anden session kan opdatere den. Derefter, inden for den samme transaktion, kan sessionen faktisk udføre en UPDATE
på samme registrering og begå eller rulle transaktionen tilbage. Dette vil give dig mulighed for at låse posten, så ingen anden session kan opdatere den, mens du måske laver en anden forretningslogik.
Dette opnås med låsning. InnoDB bruger indekser til at låse poster, så det virker nemt at låse en eksisterende post – du skal blot låse indekset for den post.
VÆLG ... TIL OPDATERING med INSERT
Men for at bruge SELECT ... FOR UPDATE
med INSERT
, hvordan låser man et indeks for en post, der ikke eksisterer endnu? Hvis du bruger standardisolationsniveauet REPEATABLE READ
, vil InnoDB også bruge gap låse. Så længe du kender id
(eller endda række af id'er) for at låse, så kan InnoDB låse hullet, så ingen anden post kan indsættes i det mellemrum, før vi er færdige med det.
Hvis dit id
kolonne var en auto-increment kolonne, derefter SELECT ... FOR UPDATE
med INSERT INTO
ville være problematisk, fordi du ikke ville vide, hvad det nye id
er var indtil du indsatte den. Men da du kender id
som du ønsker at indsætte, SELECT ... FOR UPDATE
med INSERT
vil virke.
ADVARSEL
På standard isolationsniveau, SELECT ... FOR UPDATE
på en ikke-eksisterende post ikke blokere andre transaktioner. Så hvis to transaktioner begge udfører en SELECT ... FOR UPDATE
på den samme ikke-eksisterende indekspost, får de begge låsen, og ingen af transaktionerne vil være i stand til at opdatere posten. Faktisk, hvis de prøver, vil et dødvande blive opdaget.
Derfor, hvis du ikke ønsker at håndtere et dødvande, kan du bare gøre følgende:
INDSÆT I ...
Start en transaktion, og udfør INSERT
. Lav din forretningslogik, og forpligt eller fortæl transaktionen. Så snart du gør INSERT
på det ikke-eksisterende postindeks på den første transaktion, blokeres alle andre transaktioner, hvis de forsøger at INSERT
en post med det samme unikke indeks. Hvis den anden transaktion forsøger at indsætte en post med det samme indeks efter den første transaktion begår indsættelsen, vil den få en "duplicate key"-fejl. Håndter i overensstemmelse hermed.
VÆLG ... LÅS I DELETILSTAND
Hvis du vælger med LOCK IN SHARE MODE
før INSERT
, hvis en tidligere transaktion har indsat denne post, men endnu ikke er begået, skal SELECT ... LOCK IN SHARE MODE
vil blokere, indtil den forrige transaktion er gennemført.
Så for at reducere risikoen for duplikerede nøglefejl, især hvis du holder låsene i et stykke tid, mens du udfører forretningslogik, før du begår dem eller ruller dem tilbage:
SELECT bar FROM FooBar WHERE foo = ? LOCK FOR UPDATE
- Hvis ingen poster returneres, så
INSERT INTO FooBar (foo, bar) VALUES (?, ?)