【问题标题】:What is the advantage of READ COMMITTED over READ UNCOMMITTED in Database Isolation level OR Why Dirty Read is considered so bad在数据库隔离级别上,READ COMMITTED 与 READ UNCOMMITTED 相比有什么优势,或者为什么认为脏读如此糟糕
【发布时间】:2013-04-25 08:11:11
【问题描述】:

我认为为事务选择隔离级别取决于事务的性质。但据我所知,始终建议避免(或说“受限”)使用 READ_UNCOMMITTED。大多数数据库使用 READ_COMMITTED 作为默认隔离级别。不知何故,我发现自己观察到 READ COMMITTED 优于 READ UNCOMMITTED 的优势很慢。

我看到,READ COMMITTED 相对于 READ UNCOMMITTED 的唯一优势是 READ COMMITTED 永远不会执行 DIRTY READ。我发现 DIRTY READ 只能在事务 ROLLBACK(进行脏读的事务)的情况下使数据库不一致。这意味着在事务 ROLLBACK 不太可能(或说永远不会)发生的系统的一部分中,READ UNCOMMITTED 将提供比 READ COMMITTED 更好的性能。

我们来看一个案例: 我们有记录 A = 100; B = 200;

T1(READ_UNCOMMITTED)  |    T2
                      |    A = A + 100 //(A=200 NOW)
 READ(A); //200       |
 B = B + A //400      |
 COMMIT;              |
                      |    COMMIT;

数据库不一致的唯一方法是发生事务 T2 'ROLLBACK'。现在,如果 T2 最不可能出现 ROLLBACK,那么在我看来,性能增益比承担的风险小。

提前致谢

编辑 这个评论太长了,所以@Quassnoi 假设我们有两个事务 T1(会话 1)和 T2(会话 2)。 T2 将数据库从一致性状态 DB_S1 变为 DB_S2。正如您已经知道的那样,带有 READ_UNCOMMITTED 的 T1 可能会给出与 DB_S1 或 DB_S2 都不兼容的结果。对于带有 READ_COMMITTED 的 T1,我们也可以这样说。

Lets say a schedule: T1 starts, counts 100; 
T2 starts -> has updated row 1 to .9M ;
T1 starts -> counts 150
T2 starts -> finish update
T1 starts -> can't find anything where value = 1 hence finish.

T1 给出的结果为 150,它从未存在过。

我猜在这种情况下,T1 需要一个 Searializable Lock 来保证结果的一致性,否则它将受事务调度程序的支配。

【问题讨论】:

  • 对于t1,您不需要SERIALIZABLEREAD COMMITTED 就足够了。一旦遇到被t2 锁定的第一行/第一页,它就会停止并等待t2 完成。
  • @Quassnoi 你确定吗,我在 mysql 上尝试了两个会话(不同的连接),其中 T2 获得了写锁,但 T1 仍然读取旧值。同样在您提到的情况下,T1 将计数到 100,然后通过 T2 查看提交的数据,因此结果为 100,这仍然是错误的。恕我直言,T1 需要范围锁定。
  • t1t2 不会返回,除非它们处理表中的所有记录。 t1 将返回 01M(或作为结果或死锁解决方案而回滚)。由于t1 将对已处理的记录设置共享锁,t2 将无法更新这些记录,它必须等待t1 完成。
  • 我有一个测试用例,不是针对百万条记录,但我仍然模拟重叠的事务边界。 T2(连接/会话 2){设置自动提交 = 0; SET session TRANSACTION ISOLATION LEVEL READ COMMITTED;开始交易;更新 etm.test v set v.cost = '1111' 其中 v.id = 1; /*notice no commit*/} T1(connection/session1){set autocommit = 0; SET session TRANSACTION ISOLATION LEVEL READ COMMITTED;开始交易; select v.cost from etm.test v where v.id =1;/*读取旧值*/}
  • @Quassnoi 我想你是对的,如果 T1 要求共享锁,它会给出一个符合一致性状态之一的结果。同样根据 Sql99 它应该要求一个共享锁,似乎是我深入研究 MySql 的时候了。

标签: database concurrency rdbms isolation-level


【解决方案1】:

假设您有一个包含 1M 条记录的表,所有记录都有 value = 1

然后你同时运行这两个查询:

第 1 节:

SELECT  COUNT(*)
FROM    mytable WITH (NOLOCK)
WHERE   value = 1

第 2 节:

UPDATE  mytable
SET     value = 2
WHERE   value = 1

SQL Server 中,第一个查询将返回01,000,000 之间的任意数字。

【讨论】:

  • 我想知道 READ_COMMITTED 相对于 READ_UNCOMMITTED 的优势。在您的示例中,READ_COMMITTED 将仅阻止 DIRTY_READ,但会话 1 仍可能导致会话 1 已读取的会话 2 更新的所有行的 READ_UNREPEATABLE 现象。该场景似乎不适用于 READ_UNCOMMITTED 或 READ_COMMITTED。跨度>
  • @SaurabhTripathi: 使用READ COMMITTED,对于第一个查询,您将始终获得0 行或1M。使用READ UNCOMMITTED,您将在这两者之间获得一个随机数。这可能不是您想要的第一个查询。
  • 我觉得我们可能会得到一个即使在 READ COMMITTED 也不存在的随机数。请参阅有问题的新编辑以获取我的解释
猜你喜欢
  • 1970-01-01
  • 2020-02-13
  • 2014-09-18
  • 2010-11-30
  • 2012-07-04
  • 2019-06-18
  • 1970-01-01
  • 2016-01-18
  • 1970-01-01
相关资源
最近更新 更多