Избегайте тупиков MySQL/UniDAC/Delphi

Я использую UniDAC (Devart) в 2 приложениях, обращающихся к базе данных MySQL. Во время некоторых тяжелых операций обновления, выполняемых одним приложением, иногда я получаю сообщение об ошибке "#40001 Обнаружена тупиковая ситуация при попытке получить блокировку; попробуйте перезапустить транзакцию" в другом. Прочитав советы MySQL, чтобы справиться с этим, они говорят, чтобы повторить транзакцию. Мой вопрос заключается в том, чтобы узнать лучший способ сделать это в Delphi. Я делаю это:

transaction_completed_ok:= False;
repeat
  try
    my_db.StartTransaction;
    (... do the inserts)
    my_db.Commit;
    transaction_completed_ok:= True;
  except
    my_db.Rollback;
    Sleep(1000);
  end;
until transaction_completed_ok;

Выполнение этого для каждой транзакции в обоих приложениях является правильным способом решения проблемы? Кто-нибудь может поделиться лучшим способом? Любая помощь приветствуется.

1 ответ

Ваш код для перезапуска транзакции при ошибке не может решить проблему, так как повторное выполнение одного и того же кода вызовет те же ошибки. Например, если ваш код вызывает нарушение уникальности, приложение застрянет. Чтобы решить эту проблему, вы должны реорганизовать логику приложения, чтобы избежать тупика. Взаимная блокировка возникает, когда два параллельных соединения пытаются заблокировать 2 таблицы в другом порядке, например:

connection1 locks tableA;
connection2 locks tableB;
connection1 attempts to lock tableB - waits for connection2 to unlock tableB;
connection2 attempts to lock tableA - waits for connection1 to unlock tableA.

В результате мы заходим в тупик. Чтобы избежать взаимоблокировки, вы должны установить одинаковый порядок блокировки таблиц для обоих соединений, например:

connection1 locks tableA;
connection2 locks tableA;
connection1 locks tableB;
connection2 locks tableB.

Вы можете прочитать больше информации о взаимоблокировке на http://dev.mysql.com/doc/refman/5.1/en/innodb-deadlocks.html

Для решения проблемы вы можете использовать следующий алгоритм:

  isLocked := False;

  while not isLocked do
  try
    < explicit lock tableA >
    < explicit lock tableB >
    isLocked := True;
  except
    < explicit unlock tableA >
    < explicit unlock tableB >
  end;

  if isLocked then
  try
    < do inserting to tableA and tableB >
  finally
    < explicit unlock tableA >
    < explicit unlock tableB >
  end;
Другие вопросы по тегам