Welcome 微信登录

首页 / 软件开发 / JAVA / Java理论和实践: 理解JTS ― 平衡安全性和性能

Java理论和实践: 理解JTS ― 平衡安全性和性能2011-02-04Brian Goetz在他的关于 JTS 的系列文章的第 1 和第 2 部分,Brian 讲述了一些基础知识,包括什么是事务以及 J2EE 容器如何使事务服务对 EJB 组件透明。尽管能够以声明的方式而不是编程的方式指定组件的事务性语义可以大大增强配置企业应用程序时的灵活性,但在装配应用程序时做出不当的决定会削弱应用程序的性能和稳定性。在这最后一部分,Brian 讨论了 J2EE 提供的用来管理事务划分和隔离的工具和一些高效率地使用这些工具的指导。请单击文章顶部或底部的 讨论,在 讨论论坛与作者和其他读者分享您对本文的想法。

在本系列的第 1 部分(“ An introduction to transactions”)和第 2 部分(“ The magic beind the scenes”)中,我们定义了什么是事务,列举了事务的基本特性(property),并探讨了 Java 事务服务(Java Transaction Service)和 J2EE 容器如何合作为事务提供对 J2EE 组件的透明支持。在本文中,我们将讨论事务的划分和隔离这个主题。

为 EJB 组件定义事务划分和隔离属性(attribute)的职责由应用程序装配人员来承担。如果这些属性设置不当,会对应用程序的性能、可伸缩性或容错能力造成严重的后果。不幸的是,并没有一种必须遵守的规则用于正确设置这些属性,但有一些指导可以帮助我们在并发危险和性能危险之间找到一种平衡。

我们在第 1 部分中讨论过,事务主要是一种异常处理机制。事务在程序中的用途与合法合同在日常业务中的用途相似:如果出了什么问题它们可以帮助恢复。但由于大多数时间内都没实际 发生什么错误,我们就希望能够尽量减少它们的开销以及对其余时间的占用。我们在应用程序中如何使用事务会对应用程序的性能和可伸缩性产生很大的影响。

事务划分

J2EE 容器提供了两种机制用来定义事务的起点和终点:bean 管理的事务和容器管理的事务。在 bean 管理的事务中,用 UserTransaction.begin() 和 UserTransaction.commit() 在 bean 方法中显式开始和结束一个事务。另一方面,容器管理的事务提供了更多的灵活性。通过在装配描述符中为每个 EJB 方法定义事务性属性,您可以指定每个方法的事务性需求并让容器确定何时开始和结束一个事务。无论在哪种情况下,构建事务的基本指导方针都是一样的。

进来,出去

事务划分的第一条规则是“尽量短小”。事务提供并发控制;这通常意味着资源管理器将代表您获得您在事务期间访问的数据项的锁,并且它必须一直持有这些锁,直到事务结束。(请回忆一下本系列第 1 部分所讨论的 ACID特性,其中“ACID”的“I”代表“隔离”(Isolation)。也就是说,一个事务的结果影响不到与该事务并发执行的其它事务。)当您拥有锁时,任何需要访问您锁定的数据项的其它事务将不得不一直等待,直到您释放锁。如果您的事务很长,那些其它的所有事务都将被锁定,您的应用程序吞吐量将大幅度下降。

规则 1:使事务尽可能短小。

通过使事务尽量短小,您可以把阻碍其它事务的时间缩到最短,从而提高应用程序的可伸缩性。保持事务尽可能短小的最好方法当然是不在事务中间做任何不必要耗费时间的事,特别是不要在事务中间等待用户输入。

开始一个事务,从数据库检索一些数据,显示数据,然后在仍处于事务中时请用户做出一个选择可能比较诱人。千万别这么做!即使用户注意力集中,也要花费数秒来响应 ― 而在数据库中拥有锁数秒的时间已经是很长的了。如果用户决定离开计算机,或许是去吃午餐或者甚至回家一天,会发生什么情况?应用程序将只好无奈停机。在事务期间执行 I/O 是导致灾难的秘诀。

规则 2:在事务期间不要等待用户输入。

将相关的操作归在一起

由于每个事务都有不小的开销,您可能认为最好是在单个事务中执行尽可能多的操作以使每个操作的开销达到最小。但规则 1 告诉我们长事务对可伸缩性不利。那么如何实现最小化每个操作的开销和可伸缩性之间的平衡呢?

我们把规则 1 设置为逻辑上的极端 ― 每个事务一个操作 ― 这样不仅会导致额外开销,还会危及应用程序状态的一致性。假定事务性资源管理器维护应用程序状态的一致性(请回忆一下第 1 部分,其中“ACID”的“C”代表“一致性”(Consistency)),但它们依赖应用程序来定义一致性的意思。实际上,我们在描述事务时使用的一致性的定义有点圆滑:应用程序说一致性是什么意思它就是什么意思。应用程序把几组应用程序状态的变化组织到几个事务中,结果应用程序的状态就成了 定义上的(by definition)一致。然后资源管理器确保如果它必须从故障恢复的话,就把应用程序状态恢复到最近的一致状态。

在第 1 部分中,我们给出了一个在银行应用程序中将资金从一个帐户转移到另一个帐户的示例。清单 1 展示了这个示例可能的 SQL 实现,它包含 5 个 SQL 操作(一个选择,两个更新和两个插入操作):