【问题标题】:MySQL Repeatable Read and dirty readsMySQL 可重复读和脏读
【发布时间】:2016-12-21 03:41:41
【问题描述】:

根据这个wikipedia entry,可重复读隔离级别在选择数据时持有读写锁。

我的理解是,这可以防止古老的银行业务示例:

  1. 开始交易
  2. 获取 (SELECT) 帐户余额 ($100)
  3. 提取 10 美元并更新新价值 (90 美元)
  4. 提交事务

如果在 2 和 3 之间,客户收到了 1000 美元的押金,则该交易应该被阻止,因为在第 2 步中获得了读/写锁。否则,第 3 步将写入 90 美元而不是 1090 美元。

但是,根据MySQL docs,可重复读取(默认)的工作方式不同。它所确保的是,无论我们执行多少次 SELECT,我们都会获得相同的值,无论该值是否已被另一个事务更改。也允许其他事务修改我们读取的值。

这听起来很糟糕,不知道我为什么要阅读旧的天平。该文档说需要将显式 FOR UPDATE 添加到 SELECT 以获取适当的锁。

我对可重复读取的定义和实现感到困惑。有人能澄清一下银行问题是如何解决的吗?

【问题讨论】:

  • 在这种情况下您不会使用可重复读取...事实上,如果您在事务中使用可重复读取是没有意义的。可重复读取用于“绕过”其他人更改数据,而您不想锁定,但您也不想更改数据。
  • 当您创建一个您希望在某个时间点保持内部一致性的报告时,可重复读取非常有用。如果您要更新银行帐户,则不是很有用。

标签: mysql sql transactions locking


【解决方案1】:

我将讨论它在 MySQL 和 PostgreSQL 中的工作原理,因为我对其他 SQL 实现并不熟悉。在您给出的银行示例中,应注意以下几点:

  • 存款将不会被阻止。可重复读隔离不会阻止另一个事务中的并发更改。它仅规定 read 操作在事务中的行为方式。也就是说,正如您所说,读取操作从事务开始时获取数据库的冻结版本。
  • 但是,步骤 3 中的提款操作会被存款操作阻塞,并且必须等到存款交易被提交。这种锁定发生在所有事务隔离级别,即使是最不保守的(读取未提交),因为涉及到数据更改。

现在,两笔交易完成后的余额将取决于您如何编写报表。提款步骤的SQL语句如下,

UPDATE accounts SET balance = balance - 10 WHERE id = 10;

最终余额为 1090 美元。 MySQL/PostgreSQL 将在另一个事务中意识到数据已更改,因此即使在“可重复读取”事务中调用了 UPDATE 语句,也将使用最新的值。

但是,如果您从代码中第 2 步得到的 100 美元中减去 10 美元,然后像这样运行提款

UPDATE accounts SET balance = 90 WHERE id = 10;

事务隔离无济于事,余额将变为 90 美元。

【讨论】:

    猜你喜欢
    • 2013-08-20
    • 2019-06-16
    • 2018-12-06
    • 1970-01-01
    • 1970-01-01
    • 2016-07-13
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多