首页 > 解决方案 > 竞态条件下的 INSERT-SELECT 和 UPDATE

问题描述

#1:如果我跑步

INSERT INTO foo SELECT MAX(X) FROM bar;

X我可以确定我刚刚从表中插入了该列的最大值bar吗?在零件完成后但在完成之前没有其他会话设法操纵bar表格?SELECTINSERT

#2:如果我跑步

UPDATE foo SET x = 0 WHERE y = 100;

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

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

标签: mysqlrace-conditionsimultaneous

解决方案


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

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

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


推荐阅读