Java 编程:JTS 平衡安全性和性能
事务主要是一种异常处理机制。事务在程序中的用途与合法合同在日常业务中的用途相似:
如果出了什么问题它们可以帮助恢复。但由于大多数时间内都没实 际 发生什么错误,我
们就希望能够尽量减少它们的开销以及对其余时间的占用。我们在应用程序中如何使用事
务会对应用程序的性能和可伸缩性产生很大的影响。
事务划分
J2EE 容器提供了两种机制用来定义事务的起点和终点:bean 管理的事务和容器管
理的事务。在 bean
管理的事务中,用 UserTransaction.begin()
和 UserTransaction.commit()
在 bean 方法中显式开始和结束一个事务。另一方面,容器管理的事务提供了更多的灵活
性。通过在装配描述符中为每个 EJB 方法定义事务性属性,您可以指定每个方法的事务
性需求并让容器确定何时开始和结束一个事务。无论在哪种情况下,构建事务的基本指导
方针都是一样的。
进来,出去
“
”
事务划分的第一条规则是 尽量短小 。事务提供并发控制;这通常意味着资源管理器
将代表您获得您在事务期间访问的数据项的锁,并且它必须一直持有这些 锁,直到事务
结束。(
请回忆一下本系列第 1
部分所讨论的 ACID
“
特性,其中 ACID” “
的 I”
“
代表 隔
”
离 (Isolation)。也就是说,一个事务的结果影响不到与该事务并发执行的其它事务。)当您
拥有 锁时,任何需要访问您锁定的数据项的其它事务将不得不一直等待,直到您释放锁 。
如果您的事务很长,那些其它的所有事务都将被锁定,您的应用程序吞吐量将大 幅度下
降。
规则 1:使事务尽可能短小。
通过使事务尽量短小,您可以把阻碍其它事务的时间缩到最短,从而提高应用程序
的可伸缩性。保持事务尽可能短小的最好方法当然是不在事务中间做任何不必要耗费时间
的事,特别是不要在事务中间等待用户输入。
开始一个事务,从数据库检索一些数据,显示数据,然后在仍处于事务中时请用户
做出一个选择可能比较诱人。千万别这么做!
即使用户注意力集中,也要花费数 秒来响应
― 而在数据库中拥有锁数秒的时间已经是很长的了。如果用户决定离开计算机,或许是
去吃午餐或者甚至回家一天,会发生什么情况?
应用程序将只好无奈停机。在事 务期间执
行 I/O 是导致灾难的秘诀。
规则 2:在事务期间不要等待用户输入。
将相关的操作归在一起
由于每个事务都有不小的开销,您可能认为最好是在单个事务中执行尽可能多的操
作以使每个操作的开销达到最小。但规则 1 告诉我们长事务对可伸缩性不利。那么如何实
现最小化每个操作的开销和可伸缩性之间的平衡呢?
我们把规则 1
设置为逻辑上的极端 ― 每个事务一个操作 ― 这样不仅会导致额外开
销,还会危及应用程序状态的一致性。假定事务性资源管理器维护应用程序状态的一致性
(
请回忆一下第 1
“
部分,其中 ACID” “
的 C”
“
”
代表 一致性 (Consistency)),但它们依赖应用
程序来定义一致性的意思。实际上,我们在描述事务时使用的一 致性的定义有点圆滑: