可重复读取隔离级别SELECT vs UPDATE ... WHERE

时间:2019-12-11 14:20:52

标签: mysql transactions innodb isolation-level

也许您可以在这里为我介绍一些东西:

DB = MySQL 5.7

存储引擎:InnoDB

隔离级别:重复读取

以下表格:

---------------
|   MyTable   |
---------------
| PK | Concur |
---------------
| 3  |   2    |
---------------

我目前没有任何交易,我选择这样的记录

SELECT * FROM MyTable WHERE PK = 3

并将结果存储在我的程序中。

我现在开始数据库事务。 交易开始后,外部流程将Concur = 3的记录的PK从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的记录,这是交易之前的值。

SELECTUPDATE ... WHERE的行为为何不同,我缺少什么?

我希望UPDATE ... WHERE语句直接失败而不是成功导致0条记录失败,或者它在那里成功而受到1条记录的影响,然后在COMMIT处失败,但这不是混搭。

这里有见识吗?

3 个答案:

答案 0 :(得分:1)

https://dev.mysql.com/doc/refman/8.0/en/innodb-consistent-read.html

  

一致的读取意味着InnoDB使用多版本在某个时间点向数据库呈现快照。该查询将看到在该时间点之前提交的事务所做的更改,而看不到以后或未提交的事务所做的更改。该规则的例外是查询可以看到同一事务中较早的语句所做的更改。此异常导致以下异常:如果更新表中的某些行,则SELECT会看到更新后的行的最新版本,但可能还会看到任何行的旧版本。如果其他会话同时更新了同一张表,则异常意味着您可能会以数据库中不存在的状态查看该表。

重要的条件是,如果您更改行,则一致的读取将被“刷新”,因此它包括您刚刚进行的更改。

但是,如果您执行UPDATE,则始终使用该行的最新版本,而不是事务的一致读取可以查看的版本。因此,如果另一个事务已经进行了更改,那么您的UPDATE可能没有任何效果。那就是你观察到的情况。

因此,您的交易发出了UPDATE,但没有更改该行。

也许这不是您希望InnoDB表现的方式,但是它仍然是如何表现的。

答案 1 :(得分:1)

FOR UPDATE表示您的意图:

BEGIN;
SELECT ... FOR UPDATE;
...
(no other thread can change that row until you `COMMIT` or `ROLLBACK`)
...
COMMIT;

另一方面...

BEGIN;
...
(At this point another thread modifies the row...)
...
SELECT ... FOR UPDATE;  -- you are blocked until they COMMIT or ROLLBACK

答案 2 :(得分:0)

要受到“可重复读取”隔离级别的影响,您必须处于同一事务内。这意味着您的两个选择必须在事务内部,因此无论您在外部事务中更改数据库的任何位置,它们都不会受到影响。

因此,正如您所说,我目前没有任何交易,我选择SELECT * FROM MyTable WHERE PK = 3之类的记录并将结果存储在我的程序中。仅发表一条声明。 之后,您可以使用更新开始交易。

您应该做的是

 START TRANSACTION
      SELECT * FROM MyTable WHERE PK = 3
      ------                                        START TRANSACTION
      -----                                             UPDATE MyTable SET Concur = 3 WHERE PK = 3 AND Concur = 2                                            
      -------                                       END TRANSACTTION
      -------
      -------
      SELECT * FROM MyTable WHERE PK = 3
 END TRANSACTION

在左侧,您具有选择的事务,在右侧,您具有更新。这样,更新不会影响剩余的事务。