我熟悉使用ADO(dbGo)的Microsoft SQL服务器世界,我为该环境编写了许多应用程序。现在我有一个遗留的Delphi 7应用程序和Firebird 2.5数据库,我必须维护它。
但我发现如果2个客户端应用程序执行此操作:
SQLQuery.SQL.Text := 'Update mytable set field1 = 11 where keyfield = 99'
SQLQuery.Execute;
在几乎完全相同的时间,第二个应用程序得到一个"死锁"立即出错。在SQL Server中,将有一个等待期
ADOConnection.Isolationlevel = ilCursorstability;
ADOConnection.CommandTimeout := 5;
在第二个客户端应用程序中引发任何异常之前。异常处理可能涉及在批处理过程中被视为非常不寻常的情况的回滚。这是合理的。 5秒是计算机处理时间非常长的时间。
现在,我在Firebird客户端使用相同方法的尝试没有结果,因为"死锁" (实际上,正在使用的记录)会立即发生。
如果数据库引擎无法配置等待一些条件来改进(记录锁定要发布),那么现在责任必须由客户端应用程序开发人员负责,他们必须编写疯狂的慢代码来克服出现的问题让我成为Firebird的主要失败者。
一旦"僵局"已经检测到,除了断开连接组件
之外,条件不会被清除while rowsupdated = 0 and counter < 5 do
begin
try
rowsupdated := SQLQuery.Execute;
except
SQLConnection.Connected := False;
SQLConnection.Connected := True;
end;
Inc(Counter)
end;
如果在Firebird中使用DBX中的DBX没有任何实质性的锁定容差,如何使用强大的多用户表更新客户端?
答案 0 :(得分:7)
客户端可以指定事务是否应该等待死锁解析。如果在您的情况下立即发生死锁,可能是因为您的配置(在客户端上使用nowait
事务参数)。不使用nowait
将导致服务器端检测到死锁,并且(在可配置的超时之后)在客户端上引发异常。
从Firebird 2.0开始,您还可以从客户端指定事务的锁定超时,从而覆盖服务器配置的超时值。
答案 1 :(得分:3)
可以将Firebird事务配置为nowait或wait(具有或不具有特定超时)。如何配置取决于驱动程序,因为我不熟悉Delphi我不能评论。 Nowait通常是默认值,因为在大多数情况下等待只会延迟不可避免的事情。
“死锁”错误有点用词不当,因为它不是正常并发白话中的死锁。错误的第二部分通常更具描述性,例如更新与并发更新的冲突。,它是一个历史性工件,它被归为“死锁”(尽管在大多数情况下,这些错误意味着您需要重启你的交易,所以在这个意义上它是“死”)。
答案 2 :(得分:0)
我正在使用“回答你自己的问题”按钮。我发现了一个解决方案。
这样做会导致TADOQuery.ExecSQL实际上等待(最多达到指定的15秒),如果它发现该记录已在尚未提交的事务中更新或者回滚!
这与在这种情况下立即引发所谓的“死锁”异常的DBX驱动程序不同。 (如上所述)
所以,如果两个查询都这样做
Update MYTABLE set NUM = NUM + 1 where keyvalue = 99;
并且起始值(在任何更新之前)为0,两个事务提交后的NUM值都是2,正如预期的那样。
从NUM = 0开始。如果第一个事务回滚,则第二个事务可以提交(或回滚)。并且第二次更新提交后的值仅为1.
我不知道它是如何或为什么这么好用,特别是因为Firebird不应该支持ReadUnComitted或DirtyRead,但我很高兴它按照我想要的方式工作。