Isso será difícil de reproduzir, mas espero que alguém possa esclarecer meu problema com base na lógica envolvida. Depois de ter alguns problemas intermitentes com deadlocks durante uma transação, implementei uma estratégia de repetição que é mais ou menos assim (PHP):
public function execute_query($sql) {
$try = 1;
$log_msg = '';
while (true) {
$query = $this->link->query($sql);
$error_no = $this->link->errno;
$error_msg = $this->link->error;
if (!$error_no){
if ($try > 1) {
$this->debug_log->write('Deadlock Succeeded after ' . $try . ' Tries ::: ' . $sql);
}
return $query;
} else {
$log_msg = 'Error: ' . $error_msg . ' ::: Error No: ' . $error_no . ' ::: ' . ($try > 1 ? $try . ' Tries ::: ' : '') . $sql;
$this->debug_log->write($log_msg);
if ($error_no == 1213 && $try < self::DEADLOCK_RETRY) {
// retry when deadlock occurs
sleep($try * 2);
$try++;
} else {
throw new ErrorException($log_msg);
exit();
}
}
}
}
Isso parece ter tido exatamente o efeito oposto que eu esperava - que é que parece que a primeira metade da minha transação é revertida, mas a segunda metade é confirmada.
Por exemplo:
START TRANSACTION;
Query 1
Query 2
Query 3 (deadlock occurs, query gets retried and succeeds on 2nd attempt)
Query 4
Query 5
COMMIT;
No final de tudo isso, fico com as Consultas 3, 4 e 5 sendo executadas com sucesso, mas as Consultas 1 e 2 não. Não entendo como isso é possível, mas já aconteceu duas vezes e não consegui reproduzir o impasse para testar ou desenvolver uma estratégia de trabalho.
Alguém pode explicar por que tentar novamente uma consulta com falha em uma transação InnoDB faria com que metade da transação fosse revertida e a outra metade fosse confirmada?
Quando o deadlock ocorre na consulta 3, o deadlock pode envolver a consulta 1 ou 2. Você deve recomeçar com o
START TRANSACTION
e reproduzir todas as consultas.Se algum deles for
SELECTs
e o resultado dosSELECT
controles, o que será posteriormenteUPDATEd
,DELETEd
, etc, então você temFOR UPDATE
noSELECT
?Você deve verificar se há erros após cada consulta na transação. Se ocorrer um impasse, comece de novo, conforme mencionado acima.
O que aconteceu
O que era necessário
ROLLBACK
e voltar para o arquivoSTART
.(O explícito
ROLLBACK
é provavelmente redundante, mas inofensivo. No entanto, ramificar de volta para oSTART
código do cliente.)Um "deadlock" deve ser tratado dessa maneira, mas qualquer outro "erro" deve ser tratado com um abort.
E, como um deadlock pode ocorrer após praticamente qualquer consulta, você deve testar em todos os lugares.