【问题标题】:Understanding InnoDB Repeatable Read isolation level snapshots了解 InnoDB 可重复读取隔离级别快照
【发布时间】:2021-01-27 03:23:33
【问题描述】:

我有下表:

CREATE TABLE `accounts` (
  `name` varchar(50) NOT NULL,
  `balance` int NOT NULL,
  PRIMARY KEY (`name`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_0900_ai_ci

它有两个帐户。 “Bob”的余额为 100。“Jim”的余额为 200。

我运行此查询以将 50 从 Jim 转移到 Bob:

SET TRANSACTION ISOLATION LEVEL REPEATABLE READ;
BEGIN;

SELECT * FROM accounts;

SELECT SLEEP(10);

SET @bobBalance = (SELECT balance FROM accounts WHERE name = 'bob' FOR UPDATE);
SET @jimBalance = (SELECT balance FROM accounts WHERE name = 'jim' FOR UPDATE);
UPDATE accounts SET balance = @bobBalance + 50 WHERE name = 'bob';
UPDATE accounts SET balance = @jimBalance - 50 WHERE name = 'jim';

COMMIT;

当该查询处于休眠状态时,我在另一个会话中运行以下查询以将 Jim 的余额设置为 500:

UPDATE accounts SET balance = 500 WHERE name = 'jim';

认为会发生的是,这会导致错误。该事务会将 Jim 的余额设置为 150,因为事务中的第一次读取(在 SLEEP 之前)将建立一个快照,其中 Jim 的余额为 200,并且该快照将在以后的查询中用于获取 Jim 的余额。因此,即使 Jim 的余额实际上已被其他查询更改为 500,我们也会从 200 中减去 50。

但事实并非如此。实际上,最终结果是正确的。 Bob 有 150,Jim 有 450。但我不明白为什么会这样。

MySQL 文档中提到了可重复读取:

这是 InnoDB 的默认隔离级别。同一事务中的一致性读取读取第一次读取建立的快照。这意味着如果您在同一个事务中发出多个普通(非锁定)SELECT 语句,这些 SELECT 语句也相互一致。请参阅第 15.7.2.3 节,“一致的非锁定读取”。

那么我在这里错过了什么?为什么看起来事务中的 SELECT 语句并不都是使用第一个 SELECT 语句建立的快照?

【问题讨论】:

  • 我不确定两个带有不同 where 子句的选择是否会导致读取相同的快照!对于我们人类来说,在这个特定示例中很容易确定第一个选择包含第二个和第三个选择所做的所有记录。但要以自动化的方式做到这一点并不容易!

标签: mysql sql innodb isolation-level repeatable-read


【解决方案1】:

可重复读取行为仅适用于非锁定 SELECT 查询。它从事务中第一个查询建立的快照中读取。

但任何锁定 SELECT 查询都会读取该行的最新提交版本,就好像您已在 READ-COMMITTED 隔离级别启动事务一样。

如果 SELECT 涉及修改数据的任何类型的 SQL 语句,则 SELECT 是隐式锁定读取。

例如:

INSERT INTO table2 SELECT * FROM table1 WHERE ...;

上述锁检查了 table1 中的行,即使语句只是将它们复制到 table2。

SET @myvar = (SELECT ... FROM table1 WHERE ...);

这也是将 table1 中的值复制到变量中。它将被检查的行锁定在 table1 中。

同样的 SELECT 语句在触发器中调用,或者作为多表 UPDATE 或 DELETE 的一部分,等等。只要 SELECT 是修改任何数据(在表中或变量中)的较大语句的一部分,它就会锁定 SELECT 检查的行。

因此它是一个锁定读取,并且对于它读取的行版本而言,它的行为类似于 UPDATE。

【讨论】:

    猜你喜欢
    • 2011-02-14
    • 2019-08-08
    • 1970-01-01
    • 2013-12-09
    • 1970-01-01
    • 2017-04-14
    • 1970-01-01
    • 2020-04-04
    • 2014-01-03
    相关资源
    最近更新 更多