2

当只有一个事务写入数据库时​​,什么会在 Firebird 上触发死锁消息?

我正在在 Firebird 2.1 数据库之上构建一个后端用 Delphi2010 编写的 webapp。我收到一个我无法理解的并发更新错误。也许有人可以帮助我调试问题或解释可能导致该消息的场景。

我正在尝试对单个记录上的单个字段进行更新。

UPDATE USERS SET passwdhash=? WHERE (RECID=?)

我看到的消息是标准:

deadlock
update conflicts with concurrent update
concurrent transaction number is 659718
deadlock
Error Code: 16

我明白它告诉我什么,但我不明白为什么我会在这里看到它,因为我知道没有并发更新。

这是我所做的调查。

我启动了我的应用服务器并检查了这个查询的结果:

SELECT
   A.MON$ATTACHMENT_ID,
   A.MON$USER,
   A.MON$REMOTE_ADDRESS,
   A.MON$REMOTE_PROCESS,
   T.MON$STATE,
   T.MON$TIMESTAMP,
   T.MON$TOP_TRANSACTION,
   T.MON$OLDEST_TRANSACTION,
   T.MON$OLDEST_ACTIVE,
   T.MON$ISOLATION_MODE
FROM MON$ATTACHMENTS A
LEFT OUTER JOIN MON$TRANSACTIONS T
    ON (T.MON$ATTACHMENT_ID = A.MON$ATTACHMENT_ID)

结果表明有多个连接,但其中只有一个在 MON$TRANSACTION 字段中具有非空值。这个连接是我从 IBExperts 用来查询监控表的连接。

我是否认为没有活动事务的连接可以被视为不会导致死锁情况?

接下来,我在我的应用程序服务器中提交 UPDATE-Statement 的行上放置一个断点,并执行触发它的请求。当断点停止应用程序时,我然后重新运行上面的 Monitor-query。

这一次我可以看到另一个交易活动,正如我所期望的:

两个活动交易的屏幕截图

然后我让我的应用服务器执行 UPDATE 并获得如上所示的错误消息。

当只有一个写入事务时,什么会触发死锁消息?或者还有更多,我误解了输出?关于如何调试它的任何其他建议?

4

1 回答 1

1

Firebird 使用 MVCC(多版本并发控制)作为其事务模型。其中一个功能是 - 根据事务隔离 - 您只会看到事务开始时提交的最后一个版本(一致性并发隔离级别),或者当您的语句开始时提交的版本(读取已提交)。对记录的更改将创建记录的新版本,该版本仅在提交时对其他活动事务可见(然后仅对已提交的读取事务可见)。

作为基本规则,记录只能有一个未提交的版本。因此,两个事务尝试更新同一记录的尝试对于其中一个事务将失败。由于历史原因,这些类型的错误被归入死锁错误系列,即使在正常的并发语言中它实际上并不是死锁。

根据您的事务隔离,此规则实际上更具限制性:为了一致性和并发级别,也不能有对您的事务不可见的记录的更新提交版本。

我的猜测是,对你来说,发生了这样的事情:

  1. 事务 1 开始
  2. 事务 2 以并发一致性隔离开始
  3. 事务 1 修改记录(创建新版本)
  4. 事务 1 提交
  5. 事务 2 尝试修改相同的记录

(注意,步骤 1+3 和 2 的顺序可能不同(例如 1、3、2 或 2、1、3))

第 5 步失败,因为在第 3 步中创建的新版本对事务 2 不可见。如果改为使用已提交的读取,则第 5 步将成功,因为此时新版本对事务可见。

于 2015-02-28T13:04:35.633 回答