避免死锁MySQL / UniDAC / Delphi

时间:2013-07-24 20:14:00

标签: mysql delphi delphi-xe devart unidac

我在访问MySQL数据库的2个应用程序中使用UniDAC(Devart)。 在一个应用程序进行的一些繁重的更新更新操作期间,偶尔我会在另一个应用程序中收到错误“#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 个答案:

答案 0 :(得分:2)

您在错误时重启事务的代码无法解决问题,因为重新执行同一个代码会导致相同的错误。例如,如果您的代码导致唯一性违规,则应用程序将被卡住。要解决此问题,您应该重新组织应用程序逻辑,以避免死锁。 当两个并行连接尝试以不同的顺序锁定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;