【问题标题】:How to avoid race condition in MySQL InnoDB?如何避免 MySQL InnoDB 中的竞争条件?
【发布时间】:2017-01-08 21:35:52
【问题描述】:

我们正在使用 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. 如果(总数
  3. 激活另一个灯泡; } 其他 {
  4. 回滚; }

对于这种情况,您有什么建议作为解决方案? 我们正在考虑“选择更新”,但如果我没记错的话,锁定选定的行并且用户可以在选定的行处于锁定状态时将灯泡添加到表中。

谢谢。

【问题讨论】:

  • 您需要“激活”灯泡还是什么都不做?注意甚至通知用户他不能再“激活”了? (感觉问题陈述不完整。)

标签: php mysql transactions innodb laravel-5.3


【解决方案1】:
BEGIN;   -- start a "transaction"
do the 4 steps
COMMIT;  -- or ROLLBACK

注意:任何可能导致UPDATESELECTs 都需要说FOR UPDATE

和/或,看看如何在UPDATE 中做更多的工作(见 churd 的回答)。或INSERT ... ON DUPLICATE KEY UPDATE ...

请记住,INSERTUPDATE 可以报告受影响的行数。这可以用来“假设任务会成功”,然后检查是否成功,如果失败则采取规避措施。

【讨论】:

    【解决方案2】:

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

    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

    【讨论】:

    • 我们结束了与您的建议类似的事情。我们使用单行表作为互斥体,它作为监护人并序列化所有资源访问。感谢您的建议和@Rick James 的指导。谢谢大家。
    • @JinIzzraeel - 单行互斥表通常需要 2 列:1 列用于锁定主键,1 列用于状态(空闲与谁拥有锁定的指示器)。如果您愿意,可以用您的具体情况 开始一个新问题;它们可能已经足够不同了。
    • @Rick James 感谢您的回复。单行互斥锁将充当守护者并序列化对所有资源的访问,因为代码块是对所有请求的单点访问。据我了解,这对于我们的情况应该足够了。谢谢。
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 2019-06-12
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2021-05-24
    • 1970-01-01
    相关资源
    最近更新 更多