1

High Performance Java Persistence book 的 6.3.3.3 部分中写道,丢失更新现象在 MySQL 可重复读取隔离级别中是可能的。这是屏幕截图:

在此处输入图像描述

假设如下(隔离级别为 REPEATABLE READ):

              tx1                     |                tx2
-----------------------------------------------------------------------------------
START TRANSACTION;                    |
SELECT * FROM test WHERE id = 1;      |
( say, DB_TRX_ID = 7 at this moment)   |
                                      |
                                      |  START TRANSACTION;
                                      |  SELECT * FROM test WHERE id = 1;
                                      |  UPDATE test SET name="x" WHERE id = 1;
                                      |  COMMIT;(say, makes DB_TRX_ID = 10)
                                      |
UPDATE test SET name="y" WHERE id = 1;|
COMMIT;

问题:

在 tx1 提交时,MVCC 会检测到行版本(DB_TRX_ID)不再等于 7(而不是 10)并执行回滚?或者提交将成功导致丢失更新?

4

1 回答 1

6

根据 SQL 标准,可重复读取应防止:

  • 脏读
  • 不可重复读

该标准没有提及丢失更新,因为该标准是在2PL(两阶段锁定)是事实上的并发控制机制时设计的。

如果您使用 2PL,那么可重复读取隔离级别确实可以防止丢失更新

然而,MVCC 可以通过一个元组的多个版本提供可重复读取,但是,为了防止丢失更新,他们还需要事务调度程序来跟踪某个事务读取的记录的元组修改。显然,InnoDB 不是那样工作的。

MySQL MVCC 不应该使用数据库级悲观锁定来防止丢失更新导致事务回滚

MVCC 在可重复读取中不使用任何悲观锁定。唯一采用的锁是在聚集索引上采用的间隙锁和下一个键锁,但这些锁并不能防止丢失更新。

MySQL 仅对 Serializable 使用悲观锁定,它提供了 2PL 并发控制模型,即使在使用基于 MVCC 的 InnoDB 存储引擎时也是如此。

于 2018-11-30T20:48:47.573 回答