竞争条件下的 INSERT-SELECT 和 UPDATE

INSERT-SELECTs and UPDATEs in race conditions

#1:如果我 运行

INSERT INTO foo SELECT MAX(X) FROM bar;

我可以确定我刚刚从 bar table 中插入了 X 列的最大值吗?在 SELECT 部分完成之后但在 INSERT 完成之前,没有其他会话设法操纵 bar table 吗?

#2:如果我运行

UPDATE foo SET x = 0 WHERE y = 100;

当时钟到达 00:00 并且查询需要 2 分钟时,我可以确定在 00:00 处具有 y = 100 的所有行都已更新吗?在我的查询完成之前没有其他会话设法将 y = 100 更改为 y = 80

#3:这与#2有关。如果在 00:01 另一个会话通过将 y = 99 更改为 y = 100 对一行执行 UPDATEs,我之前的查询是否尝试 UPDATE 这一行?

事务用于保证数据库的状态。可重复读取,因此当读取最大值时,您可以一次又一次地读取它,它会保持不变。

https://dev.mysql.com/doc/refman/5.6/en/innodb-transaction-isolation-levels.html#isolevel_repeatable-read

那你设置交易

https://dev.mysql.com/doc/refman/5.6/en/set-transaction.html

然后你的代码更新 x = (select max(y) from z)

然后你提交交易

我可能漏掉了一些东西,但通常情况下,它是如何进行精确报告的。

如果您不使用显式事务,则每个查询本身都被视为一个事务。因此,结合 INSERTSELECT 的查询(如您的#1)可能取决于一致性。大致相当于:

START TRANSACTION;
SET @max = (SELECT MAX(x) FROM bar);
INSERT INTO foo VALUES (@max);
COMMIT;

但是,事务不会在事务开始时创建整个数据库的快照。 InnoDB 使用每记录锁定。因此,在#2 和#3 中,如果会话 A 更新 y 而会话 B 执行您显示的查询,则 B 更新的记录可能会或可能不会包括 A 修改的记录,具体取决于 A 的相对顺序那些具体的变化。另一方面,MyISAM 使用 table 级别的锁,所以这应该是不可能的;无论哪个查询先启动,都会锁定 foo table,而另一个查询将等待它完成,然后再开始扫描 table.