【发布时间】: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,您不需要SERIALIZABLE,READ COMMITTED就足够了。一旦遇到被t2锁定的第一行/第一页,它就会停止并等待t2完成。 -
@Quassnoi 你确定吗,我在 mysql 上尝试了两个会话(不同的连接),其中 T2 获得了写锁,但 T1 仍然读取旧值。同样在您提到的情况下,T1 将计数到 100,然后通过 T2 查看提交的数据,因此结果为 100,这仍然是错误的。恕我直言,T1 需要范围锁定。
-
t1和t2不会返回,除非它们处理表中的所有记录。t1将返回0或1M(或作为结果或死锁解决方案而回滚)。由于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