MySQL
SELECT ... FOR UPDATE z aktualizacją
Korzystanie z transakcji InnoDB (auto-commit wyłączony), A SELECT ... FOR UPDATE
pozwala jedną sesję, aby tymczasowo zablokować konkretny rekord (lub rekordów) tak, że żadna inna sesja może aktualizować to.Następnie w ramach tej samej transakcji sesja może faktycznie wykonać UPDATE
na tym samym rekordzie i zatwierdzić lub wycofać transakcję. Umożliwiłoby to zablokowanie zapisu, aby żadna inna sesja nie mogła go zaktualizować, podczas gdy być może wykonasz inną logikę biznesową.
Dokonuje się tego za pomocą blokady. InnoDB wykorzystuje indeksy do blokowania rekordów, więc zablokowanie istniejącego rekordu wydaje się łatwe - wystarczy zablokować indeks dla tego rekordu.
SELECT ... FOR UPDATE z INSERT
Jednak, aby korzystać SELECT ... FOR UPDATE
z INSERT
, jak można zablokować indeksu dla rekordu, który jeszcze nie istnieje? Jeśli używasz domyślnego poziomu izolacji REPEATABLE READ
, InnoDB będzie również wykorzystywać blokady luki. Tak długo, jak znasz id
(lub nawet zakres identyfikatorów), aby zablokować, InnoDB może zablokować lukę, aby żaden inny rekord nie mógł zostać wstawiony do tej luki, dopóki nie skończymy z tym.
Jeśli kolumna id
były kolumny automatycznego przyrostu, następnie SELECT ... FOR UPDATE
z INSERT INTO
byłoby problematyczne, ponieważ nie wiedzą, co nowy id
dopiero go włożona. Ponieważ jednak znasz numer id
, który chcesz wstawić, zadziała SELECT ... FOR UPDATE
z INSERT
.
CAVEAT
na domyślnym poziomie izolacji, SELECT ... FOR UPDATE
na nieistniejącego rekordu robi nie blokowe innych transakcji. Tak więc, jeśli dwie transakcje dokonają zarówno SELECT ... FOR UPDATE
na tym samym nieistniejącym rekordzie indeksu, oba otrzymają blokadę, a żadna transakcja nie będzie w stanie zaktualizować rekordu. W rzeczywistości, jeśli obaj spróbują, zostanie wykryty zakleszczenie, a jeden zostanie wycofany.
Dlatego też, jeśli nie chcesz do czynienia z impasu, to może po prostu wykonaj następujące czynności:
INSERT INTO ...
rozpocząć transakcję i wykonać INSERT
. Wykonaj swoją logikę biznesową i zatwierdz lub wycofaj transakcję. Jak tylko wykonasz INSERT
na nieistniejącym indeksie rekordu na pierwszym transaku, wszystkie inne transakcje zostaną zablokowane, jeśli spróbują uzyskać rekord o tym samym unikalnym indeksie. Jeśli druga transakcja spróbuje wstawić rekord z tym samym indeksem po tym, jak pierwsza transakcja zatwierdzi wstawkę, otrzyma błąd "duplikatu klucza". Postępuj odpowiednio.
SELECT ... LOCK IN SHARE MODE
Jeżeli wybierzesz z LOCK IN SHARE MODE
przed INSERT
, jeśli poprzednia transakcja została włożona tego rekordu, ale jeszcze nie popełnione, SELECT ... LOCK IN SHARE MODE
będzie blokować aż do poprzedniej transakcji zostało zakończone.
Tak, aby zmniejszyć ryzyko zduplikowane kluczowych błędów, zwłaszcza jeśli posiadają zamki na chwilę podczas wykonywania logiki biznesowej przed ich zatwierdzania lub wycofywania ich z powrotem:
SELECT bar FROM FooBar WHERE foo = ? LOCK FOR UPDATE
- Jeśli żadne rekordy zwrócone, a następnie
INSERT INTO FooBar (foo, bar) VALUES (?, ?)
Does MSSQL nawet wsparcie tej składni? –
Ma "z updlockiem", który moim zdaniem jest prawie taki sam. –