如何避免MySQL InnoDB中的竞争条件?

时间:2017-01-08 21:35:52

标签: php mysql transactions innodb laravel-5.3

我们正在使用MySQL InnoDB。系统支持可同时发生的多个请求。 以下是我们试图实现的简化示例。 假设我们有一个表格,其中包含代表灯泡的实体。这些灯泡可以由用户打开(激活)。用户可以将灯泡添加到最初停用的表中,并且只有10个灯泡可以标记为已激活。所以我们有一个10的限制。由于系统支持并发,基于以下示例存在竞争条件的风险:

会议1:

1:获取活动灯泡的数量

2:检查激活的灯泡数量

3:如果尚未达到限制,请启动灯泡并提交。

4:否则回滚。

这里的问题是,在第2步和第4步中,另一个会话可能会进入并可能最终超过限制。

这是我们当前正在做的事情的SQL表示:

  1. SELECT count(id)AS total FROM tbl WHERE is_active = 1;
  2. if(total< 10){
  3. 激活另一个灯泡; } else {
  4. 回滚; }
  5. 您对此方案的解决方案有何建议? 我们正在考虑“选择更新”但是,如果我没有弄错,锁定选定的行,用户可以在选定的行处于锁定状态时向表中添加灯泡。

    谢谢。

2 个答案:

答案 0 :(得分:0)

一个想法是保留一个单独的锁定表,您可以在其中管理此限制要求,并仅对活动灯泡的数量进行原子更新,如:

update active_bulb_count
    set active_count = active_count + 1
    where active_count < limit
      and <any other conditions needed>;

如果没有更新行,您知道已达到限制,那么您就不会将灯泡设置为活动状态。

您可能仍应考虑执行此操作的会话的事务设置。查看有关原子更新和事务MySQL Atomic UPDATE in InnoDB vs MyISAM

的更多详细信息

答案 1 :(得分:0)

BEGIN;   -- start a "transaction"
do the 4 steps
COMMIT;  -- or ROLLBACK

注意:任何可能导致SELECTs的{​​{1}}需要说UPDATE

和/或,研究如何在FOR UPDATE中完成更多工作(参见churd的回答)。或UPDATE

请注意,INSERT ... ON DUPLICATE KEY UPDATE ...INSERT可以报告受影响的行数。这可以用来“假设任务成功”,然后检查它是否成功,如果失败则采取规避行动。