Zamiast FOR UPDATE
użyj LOCK IN SHARE MODE
. FOR UPDATE
uniemożliwia innym transakcjom odczytanie wiersza. LOCK IN SHARE MODE
umożliwia odczyt, ale uniemożliwia aktualizację.
Odniesienie:Podręcznik MySQL
------ sesja 1
START TRANSACTION;
SELECT * FROM test WHERE t=1 LOCK IN SHARE MODE;
UPDATE test SET NAME='irfandd' WHERE t=2;
COMMIT;
----- sesja 2 (która nie jest już blokowana :) )
START TRANSACTION;
UPDATE test SET NAME='irfandd' WHERE t=4;
COMMIT;
Aktualizacja:
Zdając sobie sprawę, że tabela nie ma indeksu na t
, mam następujące wyjaśnienie:
Po pierwsze, transakcja T1 blokuje wiersz 1 w SELECT * FROM test WHERE t=1 FOR UPDATE
Następnie transakcja T2 próbuje wykonać UPDATE test SET NAME='irfandd' WHERE t=4
. Aby dowiedzieć się, których wierszy dotyczy problem, musi przeskanować wszystkie wiersze, w tym wiersz 1 . Ale to jest zablokowane, więc T2 musi poczekać, aż T1 się zakończy. Jeśli istnieje jakikolwiek indeks, WHERE t=4
może użyć indeksu, aby zdecydować, czy wiersz 1 zawiera t=4
czy nie, więc nie trzeba czekać.
Opcja 1: dodaj indeks na test.t
aby Twoja aktualizacja mogła z niego korzystać.
Opcja 2: użyj LOCK IN SHARE MODE
, który jest przeznaczony do nakładania tylko blokady odczytu. Niestety ta opcja powoduje zakleszczenie. Co ciekawe, transakcja T2 jest wykonywana (aktualizacja wiersza 4), a T1 kończy się niepowodzeniem (aktualizacja wiersza 2). Wygląda na to, że T1 blokuje odczyt wiersza 4 również, a ponieważ T2 go modyfikuje, T1 kończy się niepowodzeniem z powodu poziomu izolacji transakcji (POWTARZALNY ODCZYT domyślnie
). Ostatecznym rozwiązaniem byłaby gra z Poziomami izolacji transakcji , używając READ UNCOMMITTED
lub READ COMMITTED
poziomy transakcji.
Najprostsza to Opcja 1 , IMHO, ale to zależy od Twoich możliwości.