SQL并发'选择然后更新'具有READ_COMMITED隔离级别的模式

时间:2015-02-12 15:41:28

标签: java sql concurrency transactions isolation-level

我们有一个运行在mysql 5.5数据库InnoDb引擎上的集群Java应用程序。该应用程序使用Spring,事务隔离级别设置为SERIALIZABLE

在一个方法中,可能并发执行,无论是在一个节点的不同线程中还是在不同的节点中,都有一个典型的SELECT-then-UPDATE模式。这是伪代码:

old_status = null;
do {
    old_status = SELECT status 
                 FROM bookings 
                 WHERE code=123;

    affected_rows = UPDATE bookings 
                    SET status=<new_status> 
                    WHERE code=123 AND status=<old_status>;

} while (affected_rows == 0);

// Now we can do stuff with <old_status> value

字段code是PK; <old_status><new_status>始终不同。

SELECTUPDATE分开的交易中运行。

这非常有效。我甚至认为AND status=<old_status>语句的WHERE子句中的UPDATE检查是不必要的,因为事务隔离级别是SERIALIZABLE,这意味着发布它是安全的。 SELECT然后UPDATE,而不检查status字段的值。

所以,第一个问题:(1)当事务隔离级别为SERIALIZABLE时,我是否可以接受所有这些是不必要的?

现在,发生的事情是,几天前,DBA突然在我的工作场所实现,并附带了&#39;证明&#39; 我们应该开始使用的论据和指标{{ 1}}事务隔离级别。由于他几乎从不离开他的地下墓穴,我立刻知道他是认真的。他说,如果我们将事务隔离级别从READ_COMMITED更改为SERIALIZABLE,则cpu的使用率将下降10%,并且查询将以更快的速度运行,等等。总之,他有他的使我们切换到READ_COMMITED事务隔离级别的参数。

所以,第二个问题:(2)我们可以愉快地切换到READ_COMMITED事务隔离级别,因为我们已经检查了READ_COMMITED字段的值{ status WHERE句的{1}}条款通过UPDATE

我认为我们可以,但我想首先与社区核实。提前谢谢!

注意:我无法使用本机构造,例如mysql的SELECT ... FOR UPDATE等。

2 个答案:

答案 0 :(得分:1)

首先。在序列化隔离中比较旧状态是不必要的。如果读取锁被同一数据元素上的另一个事务的写锁定覆盖,或者写锁定被延迟,则序列化事务将失败,具体取决于数据库中使用的实现。

此外,我高度赞同10%的读取权利要求。

我也不是读书的朋友。我在一家保险公司工作,新系统全部使用Read Committed。他们使用Select进行更新。我作为一个受过交易管理和数据库培训的人(大学的主要科目)我非常不喜欢使用read committed isolation进行写入事务。对于只使用选择它的读取交易,你可以,但重点在哪里。

我认为read committed只保存撤消信息(可能),并且会减少事务需要回滚的机会(Douful)。

还有第三个建议。使用两个数据源。一个使用读提交的隔离级别,另一个使用序列化。使用第二个执行所有写入事务,第一个执行其余操作。

但是你又遇到了各种各样的麻烦。由于使用了read committed,因此相同数据可能会失败。正如我所知,10%与工程师在没有意识到新限制的情况下制作成交易代码的所有错误相比毫无意义。

在您的数百和数千笔交易的集合中只需要一个误解,您可能会面临一个错误,这将花费您数十到数千小时的搜索。我知道在我们记录隔离级别并看到拼图中缺失的部分之前从未发现的缺陷。

编写交易很容易,但正确处理非常困难。

所以我的建议是:只要你没有任何关于10%数据库的性能问题就不要改变隔离级别。如果您遇到性能问题,请升级硬件。硬件很便宜,只要它不是来自IBM,Oracle或HP等等。

记住100小时的时间,你可以在你的机器中添加一个TB的RAM(如果它来自IBM,Oracle或HP或其他任何东西,则增加100GB),而这些100GB将比隔离级别提高你的性能。 / p>

除非你需要它或者你是一个大师,否则不要乱用它。对于保险软件,他们使用SQL行进行悲观锁定,并使用select进行更新。每个都是性能杀手,因为它们是瓶颈。

您的解决方案很好,但您不需要比较。

最好的解决方案是:

` [开始阅读交易]     选择 * .... [/ end read transaction]

计算变化

[开始写交易] 比较和设置使用更新...其中选择... [结束写交易] `

我认为您在while循环中执行此事务处理,否则您的代码将不太好:)。

答案 1 :(得分:1)

如果两者都在单独的交易中,则需要比较旧值。你所做的是乐观应用程序事务中的一种简单技术,称为比较和设置。所以是的你可以随意使用它。

关于Read committed隔离级别,它在事务中唯一有趣。如果您只有一个选择或更新,如果我正确理解了Oracles的实现,它根本不会有任何区别。 Oracle只访问表中的一行,并将重用已经获得的那些信息。但它们可能是无法重用这些信息的情况(会话缓存的条款等)。

所以它需要高水平的理解,不应该在没有考虑关于某些变化的影响的每一个交易的情况下完成,我的意思是所有潜在的变化。因此,如果您希望这样做,您可以看到您的成本飙升。

如果你没有为它设计,我永远不会轻易改变隔离级别。