【问题标题】:Database race conditions数据库竞争条件
【发布时间】:2012-04-08 16:05:21
【问题描述】:

我听说许多应用程序开发人员在数据库处理中的竞争条件方面遇到了一些麻烦。一个典型的例子是这样的:

  • 用户 1 选择一个字段,例如 numStock,即 3
  • 用户 2 也选择了 numStock,仍然是 3
  • 用户 1 递减 numStock(在应用程序中),并在数据库中将其设置为 2。
  • 用户 2 还会减少 numStock(在应用程序中),并在数据库中将其设置为 2。

在本例中,numStock 字段本应为 1,但由于用户之间的竞争,它被设置为 2。

所以当然可以使用锁,但我想到了另一种处理方式 - 将所有行详细信息作为 WHERE 标准传递。让我解释一下……

在上面的示例中,SQL 代码可能如下所示:

//选择

SELECT itemID, numStock FROM items WHERE itemID = 45

//更新

UPDATE items SET numStock = 2 WHERE itemID = 45

我解决比赛的想法:

//选择

SELECT itemID, numStock FROM items WHERE itemID = 45

//更新

UPDATE items SET numStock = 2 WHERE itemID = 45 AND numStock = 3

因此,查询会检查数据在选择数据后是否发生了变化。 所以我的问题是: (1) 这[总是] 有效吗? (2) 与数据库锁定机制(例如 MySQL 事务)相比,这是一个更好的选择吗?

感谢您的宝贵时间。

【问题讨论】:

  • 对于您的情况,您可以使用增量而不是将其设置为特定值。这样,如果两个单独的进程都想增加 numStock,它们将不会相互竞争。 SET numStock = numStock + 1

标签: database race-condition transaction-isolation


【解决方案1】:

与数据库的每次交互都是一个事务 - 在您只编写单个语句时是隐式的,或者在您使用 BEGIN / COMMIT / ROLLBACK 时是显式的。

然后您使用事务隔离级别,它定义了可能发生的现象。典型的现象称为脏读、不可重复读、幻读。典型的隔离级别是 READ_COMMITTED、REPEATABLE_READ、SERIALIZABLE。

让我们看看你的具体例子(时间流逝):

T1                T2
-----------------------------
x := r[numStock]
                  y := r[numStock]
w[numStock] = x-1
                  w[numStock] := y-1

因此,T2 的写入是在陈旧数据上。这是丢失的更新。一些数据库,例如 Postgres,可以防止丢失更新。当尝试在REPEATABLE_READ或更高隔离级别提交事务时,会抛出异常:

错误:由于并发更新,无法序列化访问

但是,我听说 MySQL 中的 InnoDB 引擎没有检查丢失更新 (source)。

提到的事务隔离级别指定了您要防止的问题。他们没有就如何实现它做出任何声明。有乐观并发控制(快照隔离)和悲观并发控制(锁)。

我很快也会发表一篇关于这些主题的文章 :-)

【讨论】:

    【解决方案2】:

    如何在单个语句中进行选择和更新:

    UPDATE counters SET value = (@cur_value := value) + 1 WHERE name_counter = 'XXX';
    

    然后

    SELECT @cur_value;
    

    此策略是否可以解决竞争条件?

    【讨论】:

      【解决方案3】:

      这种策略有效,被称为“乐观锁定”。那是因为您在进行处理时假设它会成功,并且只有在最后才实际检查它是否成功。

      当然,您需要一种重试事务的方法。如果失败的可能性非常高,它可能会变得低效。但在大多数情况下,它工作得很好。

      【讨论】:

      • 单数据库连接时是否会出现数据库争用情况?
      • 一些数据库包含运行异步进程的选项,使用这些功能,您甚至可以通过单个连接获得竞争条件。当然,根据您的观点,这些过程可能算作单独的连接。 ...
      猜你喜欢
      • 2013-08-12
      • 1970-01-01
      • 2011-09-22
      • 1970-01-01
      • 2022-01-23
      • 2017-07-24
      • 2014-10-07
      • 1970-01-01
      • 2010-10-10
      相关资源
      最近更新 更多