Em vez de
FOR UPDATE
use LOCK IN SHARE MODE
. FOR UPDATE
impede que outras transações leiam a linha também. LOCK IN SHARE MODE
permite a leitura, mas impede a atualização. Referência:Manual MySQL
------ sessão 1
START TRANSACTION;
SELECT * FROM test WHERE t=1 LOCK IN SHARE MODE;
UPDATE test SET NAME='irfandd' WHERE t=2;
COMMIT;
----- sessão 2 (que não está mais sendo bloqueada :))
START TRANSACTION;
UPDATE test SET NAME='irfandd' WHERE t=4;
COMMIT;
Atualização:
Percebendo que a tabela não tem índice em
t
, tenho a seguinte explicação:Primeiro, a transação T1 bloqueia a linha 1 em
SELECT * FROM test WHERE t=1 FOR UPDATE
Em seguida, a transação T2 tenta executar
UPDATE test SET NAME='irfandd' WHERE t=4
. Para descobrir quais linhas são afetadas, ele precisa verificar todas as linhas, incluindo linha 1 . Mas isso está bloqueado, então T2 deve esperar até que T1 termine. Se houver algum tipo de índice, o WHERE t=4
pode usar o índice para decidir se a linha 1 contém t=4
ou não, então não há necessidade de esperar. Opção 1: adicione um índice em
test.t
para que sua atualização possa usá-lo. Opção 2: use
LOCK IN SHARE MODE
, que se destina a colocar apenas um bloqueio de leitura. Infelizmente esta opção cria um impasse. Curiosamente, a transação T2 é executada (atualizando a linha 4) e a T1 falha (atualizando a linha 2). Parece que o T1 bloqueia a linha 4 também, e como o T2 o modifica, o T1 falha devido ao nível de isolamento da transação (LEITURA REPETÍVEL por padrão
). A solução final seria brincar com Níveis de isolamento de transação , usando READ UNCOMMITTED
ou READ COMMITTED
níveis de transação. A mais simples é a Opção 1 , IMHO, mas depende de suas possibilidades.