【问题标题】:MySQL InnoDB concurrent updatesMySQL InnoDB 并发更新
【发布时间】:2017-03-10 17:19:34
【问题描述】:

您有一个 InnoDB 表 A,其中包含以下两列:id (PK)、状态

两个线程按以下顺序访问表:

Thread 1: SELECT id from A where status = 0 LIMIT 1;
Thread 2: SELECT id from A where status = 0 LIMIT 1;

两个线程都选择 id 1。

Thread 1: UPDATE A SET status = 1 WHERE id = 1 AND status = 0;
Thread 2: UPDATE A SET status = 2 WHERE id = 1 AND status = 0;

两个线程是否有可能更新同一行?

现在添加交易:

Thread 1: SELECT id from A where status = 0 LIMIT 1;
Thread 2: SELECT id from A where status = 0 LIMIT 1;
Thread 1: START TRANSACTION;
Thread 2: START TRANSACTION;
Thread 1: UPDATE A SET status = 1 WHERE id = 1 AND status = 0;
Thread 2: UPDATE A SET status = 2 WHERE id = 1 AND status = 0;
Thread 1: COMMIT;
Thread 2: COMMIT;

现在两个线程都可以更新行吗?

在任何一种情况下,如果两行都可以更新,解决此问题的正确方法是什么?

【问题讨论】:

标签: mysql multithreading


【解决方案1】:

InnoDB 上的默认锁定机制是 REPEATABLE_READ(参见 https://dev.mysql.com/doc/refman/5.7/en/innodb-transaction-isolation-levels.html#isolevel_repeatable-read)。这意味着如果您对同一个索引进行两次更新将自动完成,但是我不相信您可以保证哪个更新将首先通过,而不在您的程序中添加一些逻辑。 (第一次更新将由线程首先获取锁)。

因此,要管理以正确顺序完成更新,您需要在程序中实现逻辑(如果是 Java,我会查看 BarrierLatchesSemaphores)。

【讨论】:

  • 谢谢!我不太关心订单,我担心只有一个更新是可能的(因此 where 的 status=0 )。也就是说,第一次更新后获取锁的线程是否可以看到status值的变化?
  • 好的,那么默认情况下肯定可以进行两次更新
  • 将事务隔离改为 Serializable 会解决这个问题吗?
  • Serializable 将不起作用,因为该行将在另一个对状态 0 进行选择之前更新为状态 1 或 2(因此以空行结束)。因此,如果您真的想要这种行为(我建议不要),默认级别是可以的,但是,您需要确保在任何更新发生之前完成每个选择(请参阅我的帖子中使用信号量等的部分)跨度>
  • 我所追求的行为(可能应该在问题中更清楚地说明这一点)是我只希望两个线程中的一个能够更新该行。想象一下,这些行代表一些只能使用一次的资源。如果两者都得到了那一行,我希望最后到达那里的人失败并再次尝试找到新行。如果我能从一开始就保证这一点,那就太好了。
猜你喜欢
  • 2014-03-30
  • 2011-10-18
  • 2017-02-17
  • 1970-01-01
  • 2012-02-20
  • 2019-08-02
  • 1970-01-01
  • 1970-01-01
  • 2012-07-15
相关资源
最近更新 更多