MySQL
WYBIERZ... DO AKTUALIZACJI za pomocą AKTUALIZACJI
Korzystanie z transakcji z InnoDB (automatyczne zatwierdzanie wyłączone), SELECT ... FOR UPDATE
umożliwia jednej sesji tymczasowe zablokowanie określonego rekordu (lub rekordów), aby żadna inna sesja nie mogła go zaktualizować. Następnie, w ramach tej samej transakcji, sesja może faktycznie wykonać UPDATE
w tym samym rekordzie i zatwierdź lub wycofaj transakcję. Umożliwiłoby to zablokowanie rekordu, aby żadna inna sesja nie mogła go zaktualizować, podczas gdy być może wykonasz inną logikę biznesową.
Osiąga się to za pomocą blokowania. InnoDB wykorzystuje indeksy do blokowania rekordów, więc blokowanie istniejącego rekordu wydaje się łatwe — po prostu zablokuj indeks dla tego rekordu.
WYBIERZ... DO AKTUALIZACJI za pomocą WSTAW
Jednak, aby użyć SELECT ... FOR UPDATE
z INSERT
, jak zablokować indeks dla rekordu, który jeszcze nie istnieje? Jeśli używasz domyślnego poziomu izolacji REPEATABLE READ
, InnoDB będzie również wykorzystywać gap zamki. Tak długo, jak znasz id
(lub nawet zakres identyfikatorów) do zablokowania, InnoDB może zablokować przerwę, aby żaden inny rekord nie mógł zostać wstawiony w tę przerwę, dopóki nie skończymy z tym.
Jeśli Twój id
kolumna była kolumną z automatycznym przyrostem, a następnie SELECT ... FOR UPDATE
z INSERT INTO
byłoby problematyczne, ponieważ nie wiedziałbyś, jaki jest nowy id
było, dopóki go nie włożysz. Jednak ponieważ znasz id
który chcesz wstawić, SELECT ... FOR UPDATE
z INSERT
zadziała.
OSTRZEŻENIE
Na domyślnym poziomie izolacji SELECT ... FOR UPDATE
na nieistniejącym rekordzie nie blokować inne transakcje. Tak więc, jeśli dwie transakcje wykonają SELECT ... FOR UPDATE
w tym samym nieistniejącym rekordzie indeksu, oboje otrzymają blokadę i żadna transakcja nie będzie w stanie zaktualizować rekordu. W rzeczywistości, jeśli spróbują, zostanie wykryty impas.
Dlatego, jeśli nie chcesz radzić sobie z impasem, możesz po prostu wykonać następujące czynności:
WSTAW DO...
Rozpocznij transakcję i wykonaj INSERT
. Wykonaj logikę biznesową i zatwierdź lub wycofaj transakcję. Jak tylko wykonasz INSERT
na nieistniejącym indeksie rekordu przy pierwszej transakcji, wszystkie inne transakcje zostaną zablokowane, jeśli spróbują INSERT
rekord z tym samym unikalnym indeksem. Jeśli druga transakcja spróbuje wstawić rekord o tym samym indeksie po tym, jak pierwsza transakcja zatwierdzi wstawienie, otrzyma błąd „duplikat klucza”. Postępuj odpowiednio.
WYBIERZ... ZABLOKUJ W TRYBIE UDOSTĘPNIANIA
Jeśli wybierzesz za pomocą LOCK IN SHARE MODE
przed INSERT
, jeśli poprzednia transakcja wstawiła ten rekord, ale jeszcze nie została zatwierdzona, SELECT ... LOCK IN SHARE MODE
zostanie zablokowany do czasu zakończenia poprzedniej transakcji.
Aby zmniejszyć ryzyko zduplikowanych błędów kluczy, zwłaszcza jeśli przytrzymasz blokady na chwilę podczas wykonywania logiki biznesowej przed ich zatwierdzeniem lub wycofaniem:
SELECT bar FROM FooBar WHERE foo = ? LOCK FOR UPDATE
- Jeśli nie zostaną zwrócone żadne rekordy,
INSERT INTO FooBar (foo, bar) VALUES (?, ?)