可重复读取隔离级别SELECT VS UPDATE...WHERE
也许你可以在这里为我解释一些事情:
DB=MySQL 5.7
存储引擎:InnoDB
隔离级别:可重复读取
下表:
---------------
| MyTable |
---------------
| PK | Concur |
---------------
| 3 | 2 |
---------------
此时我没有正在进行的事务,我选择此记录,如下所示
SELECT * FROM MyTable WHERE PK = 3
并将结果存储在我的程序中。
我现在启动一个数据库事务。 事务开始后,外部进程将PK
=3的记录的Concur
从2递增到3。
我尚未再次从事务内的该表中读取。
我从我的事务内部发出以下查询:
UPDATE MyTable SET Concur = 3 WHERE PK = 3 AND Concur = 2
这将通过0 records affected
成功。很明显,它会根据我的事务开始后更改的数据进行评估。
仍在事务中,我随后查询:
SELECT * FROM MyTable WHERE PK = 3
它将向我返回PK = 3 and Concur = 2
的记录,这些值是事务之前的值。
为什么SELECT
和UPDATE ... WHERE
的行为不同,我缺少什么?
我认为UPDATE ... WHERE
语句要么直接失败,而不是在0条记录受到影响的情况下成功,要么在那里成功,但有1条记录受到影响,然后在随后的COMMIT
处失败,但不是这种混合和匹配。
这里有什么见解吗?
解决方案
https://dev.mysql.com/doc/refman/8.0/en/innodb-consistent-read.html
一致读取意味着InnoDB使用多版本控制向查询呈现某个时间点的数据库快照。查询会看到在该时间点之前提交的事务所做的更改,而不会看到后来或未提交的事务所做的更改。此规则的例外情况是,查询会看到同一事务中较早的语句所做的更改。此异常会导致以下异常:如果更新表中的某些行,SELECT会看到更新的行的最新版本,但也可能会看到任何行的较旧版本。如果其他会话同时更新同一个表,则异常情况意味着您可能会看到该表处于数据库中不存在的状态。
重要的条件是,如果您更改了行,您的一致读取将被"刷新",因此它包括您刚刚所做的更改。
但如果您更新,它始终是行的最新版本,而不是事务的一致读取可以查看的版本。因此,如果另一个事务已经进行了该更改,则您的更新可能不会产生任何净效果。这就是您观察到的情况。
因此,您的事务发出了更新,但未更改行。
这可能不是您希望InnoDB的行为方式,但它确实是这样的。
相关文章