Как справиться с тупиковыми ситуациями MySQL на уровне приложений? - PullRequest
17 голосов
/ 12 ноября 2011

Когда возникает ситуация взаимоблокировки в MySQL / InnoDB, он возвращает эту знакомую ошибку:

'Обнаружен тупик при попытке получить блокировку; попробуйте перезапустить транзакцию '

Итак, я сделал запись всех запросов, которые входят в транзакцию, чтобы их можно было просто переиздать в случае сбоя оператора в транзакции. Простой.

Проблема: если у вас есть запросы, которые зависят от результатов предыдущих запросов, это не так хорошо работает.

Например:

START TRANSACTION;
INSERT INTO some_table ...;
-- Application here gets ID of thing inserted: $id = $database->LastInsertedID()
INSERT INTO some_other_table (id,data) VALUES ($id,'foo');
COMMIT;

В этой ситуации я не могу просто переиздать транзакцию, как она была изначально создана. Идентификатор, полученный первым оператором SQL, больше не действителен после сбоя транзакции, но используется вторым оператором. Между тем, многие объекты были заполнены данными из транзакции, которые затем становятся устаревшими при откате транзакции. Сам код приложения, конечно, не «откатывается» с базой данных.

Вопрос: как я могу справиться с этими ситуациями в коде приложения? (PHP)

Я предполагаю две вещи. Скажите, пожалуйста, если вы считаете, что я на правильном пути:

1) Поскольку база данных не может просто переписать транзакцию дословно во всех ситуациях, мое оригинальное решение не работает и не должно использоваться.

2) Единственный хороший способ сделать это - обернуть любой и весь код выдачи транзакции в собственный блок try / catch и попытаться переиздать сам код, а не только SQL.

Спасибо за ваш вклад. Вы качаетесь.

1 Ответ

11 голосов
/ 12 ноября 2011

Транзакция может быть неудачной.Deadlock - это случай сбоя, вы также можете иметь больше сбоев на сериализуемых уровнях.Проблемы изоляции транзакций - это кошмар.Я думаю, что пытаться избежать неудач - плохой способ.

Я думаю, что любой хорошо написанный код транзакции должен быть эффективно подготовлен к неудачным транзакциям.

Как вы уже видели, запись запросов и их воспроизведение нерешение, как при перезапуске транзакции база данных была перемещена.Если бы это было верное решение, движок SQL наверняка сделал бы это для вас.Для меня правила таковы:

  • повторить все ваши чтения внутри транзакций (любые данные, которые вы прочитали снаружи, возможно, были изменены)
  • выбросить все с предыдущей попытки, если вы написаливещи за пределами транзакции (журналы, LDAP, что-либо вне SGBD) должны быть отменены из-за отката
  • повторить все на самом деле: -)

Это означает Повторяющийся цикл .

Итак, у вас есть блок try / catch с транзакцией внутри.Вам нужно добавить цикл while, возможно, с 3-мя попытками, вы покидаете цикл while, если коммитная часть кода выполнена успешно.Если после 3-х повторных попыток транзакция все еще не выполняется, запустите для пользователя Исключение - чтобы вы не пытались выполнить бесконечный цикл повторных попыток, на самом деле у вас может быть действительно большая проблема -.Обратите внимание, что вы должны обрабатывать ошибки SQL и блокировать или сериализуемое исключение по-разному.3 - произвольное число, вы можете попробовать большее количество попыток.

Это может дать что-то вроде этого:

$retry=0;
$notdone=TRUE;
while( $notdone && $retry<3 ) {
  try {
    $transaction->begin();
    do_all_the_transaction_stuff();
    $transaction->commit();
    $notdone=FALSE;
  } catch( Exception $e ) {
    // here we could differentiate basic SQL errors and deadlock/serializable errors
    $transaction->rollback();
    undo_all_non_datatbase_stuff();
    $retry++;
  }
}
if( 3 == $retry ) {
  throw new Exception("Try later, sorry, too much guys other there, or it's not your day.");
}

И это означает, что все вещи (чтение, запись, функциональные вещи) должен быть заключен в $do_all_the_transaction_stuff();.Подразумевается, что код управления транзакциями находится в контроллерах, высокоуровневый код приложения-функционала-основного * , не разбитый на несколько низкоуровневых моделей доступа к базе данных .

...