【问题标题】:Deadlocks and Timeouts break ACID transactions死锁和超时会破坏 ACID 事务
【发布时间】:2011-07-13 07:57:13
【问题描述】:

我有一个像这样工作的事务性应用程序:

try {
     $db->begin();
     increaseNumber();
     $db->commit();
} catch(Exception $e) {
     $db->rollback();
}

然后在 increaseNumber() 中,我将有一个这样的查询,这是唯一适用于该表的函数:

// I use FOR UPDATE so that nobody else can read this table until its been updated
$result = $db->select("SELECT item1
FROM units
WHERE id = '{$id}'
FOR UPDATE");

$result = $db->update("UPDATE units SET item1 = item1 + 1
WHERE id = '{$id}'");

所有内容都包含在事务中,但最近我一直在处理一些非常慢的查询,并且我的应用程序中有很多并发性,所以我无法确保查询是以特定顺序运行的.

死锁会导致 ACID 事务中断吗?我有一个添加某些内容的函数,然后是另一个删除它的函数,但是当我遇到死锁时,我发现数据完全不同步,就像忽略了事务一样。

这一定会发生还是有其他问题?

谢谢,多米尼克

【问题讨论】:

    标签: php mysql transactions acid database-concurrency


    【解决方案1】:

    好吧,如果一个事务遇到了一个没有释放的锁(来自另一个事务),它会在超时后失败。我相信默认值为 30 秒。如果有人在数据库上使用任何 3rd 方应用程序,您应该注意。我知道一个事实,例如,SQL Manager 2007 不会释放 InnoDB 上的锁,除非您断开与数据库的连接(有时它只需要在 ...好吧,一切),这会导致很多查询在超时后失败。当然,如果您的事务是 ACID 兼容的,它应该以全有或全无的方式执行。只有在事务之间中断数据时才会中断。

    您可以尝试延长超时时间,但 30 秒的锁定可能意味着一些更深层次的问题。当然,这取决于您使用的存储引擎(通过MySQL 标记和我假设为 InnoDB 的事务)。

    您还可以尝试打开query profiling 以查看是否有任何查询运行了很长的时间。请注意,它当然会降低性能,因此它可能不是生产解决方案。

    【讨论】:

    • 嗯,我没想到。因此,如果我使用 MySQL Workbench 并处理行并且我没有在生产中提交事务,那么我就持有锁。我猜会调查这个
    • 你可以用SHOW FULL PROCESSLIST查一下,看看有没有什么可疑的地方。
    【解决方案2】:

    ACID 中的 A 代表 Atomic,因此没有死锁不能使 ACID 事务中断 -- 相反,它不会像全有或全无一样发生。

    更有可能的是,如果您的数据不一致,您的应用程序在逻辑单个事务中执行多个“事务”,例如:用户创建和帐户 (transaction-begin..-commit),用户设置密码 (transaction- begin...-deadlock..-rollback) 您的应用程序忽略了错误并继续,现在您的数据库留下了一个创建的用户并且没有密码。

    在您的应用程序中查看应用程序除了回滚之外还在做什么,以及逻辑上是否存在多个部分来构建一致的数据。

    【讨论】:

    • 嘿,我添加了一个正在使用的查询示例(仅在此位置使用,并且仅包含在事务中)。我认为死锁可能会破坏事务的原因是当我确实遇到很多死锁时,该单位表中的数据不一致。不过我很困惑,因为它只在这个地方使用过……也许是 FOR UPDATE 的问题,它允许 2 人在死锁释放锁时编辑数据?
    • 当您怀疑死锁导致不一致时,您究竟看到了什么错误?据我从上面的代码中可以看出,您只更新了一条记录,因此在 ACID 事务结束时,它要么更新要么不更新——“或不”是回滚的结果。不一致需要在同一事务中发生两次更新,您会看到一个更新发生但没有另一个更新 - 我在您的代码中没有看到类似的内容。
    • 我有一张需要执行的任务表。如果两个人查询任务表,我使用 FOR UPDATE 来确保只有一个人执行任务。一个人可能将 item1 设置为 1,然后另一个人可能想将其设置为 0。当出现死锁时,item1 的值有时可能是 2、3、4、5、6,而它应该永远是 0 或 1,所以我感觉当发生死锁时,将其设置为 0 的事务不成功,它变为 1 + 1 = 2。
    • 但是您没有将其设置为 1 或 0 的代码,只有在其他地方将其设置为 value+1(和可能的 value-1)的代码。那么你如何保证这个值只能是 0 或 1?如果答案是你刚刚在前面的语句中选择了它,然后在代码中检查了它,那么检查数据库格式是否保证读取一致性(我相信isam和innodb的行为不同,沿着@Naltharial的思路扩展思路)跨度>
    • 如果可能,尝试将 select 和 update 合并为一个语句?这行得通吗?
    猜你喜欢
    • 2014-02-25
    • 2012-06-28
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2014-12-12
    • 2015-05-03
    • 1970-01-01
    相关资源
    最近更新 更多