事务悬停 Transaction Suspension
原文链接: https://docs.oracle.com/cd/E26180_01/Platform.94/ATGProgGuide/html/s1204transactionsuspension01.html
source: https://docs.oracle.com/cd/E26180_01/Platform.94/ATGProgGuide/html/s1204transactionsuspension01.html
When a transaction is created, it is associated with the thread that created it. As long as the transaction is associated with the thread, no other transaction can be created for that thread.
当一个事务被创建后,它就与创建它的线程关联在了一起。只要当前线程已经关联有一个事务,就不能再为该线程创建新的事务了。
Sometimes, however, it is helpful to use multiple transactions in a single set of actions. For example, suppose a request performs some database operations, and in the middle of those operations, it needs to generate an ID for a database row that it is about to insert. It generates the ID by incrementing a persistent value that it stores in a separate database table. The request continues to do some more database operations, then ends.
但有些时候,使用多个事务去完成一连串动作也是非常有用的。举个例子,假设现在需要执行一连串数据库操作,中间有一步是为将要插入的一行数据生成一个 ID。而生成 ID 的方式是引用存储在另一张表中的一个值,并加 1。生成完 ID 后,再执行后续的数据库操作,随后结束。
All of this can be done in a single transaction. However, there is a potential problem with placing the ID generation within that transaction. After the transaction accesses the row used to generate the ID, all other transactions are locked out of that row until the original transaction ends. If generating IDs is a central activity, the ID generation can end up being a bottleneck. If the transaction takes a long time to complete, the bottleneck can become a serious performance problem.
这一连串的数据库操作当然完全可以放在一个单独的事务中执行。但是,如果不把 ID 生成这一步操作放置到独立的一个事务中去,就会存在一个隐患。当事务触及到生成 ID 的那一行数据时,所有其他的数据都会被锁在行外,直到当前事务结束为止。如果生成 ID 是一个核心操作,那这一步操作就极有可能成为系统的性能瓶颈。如果事务需要很长的时间才能完成,那么性能瓶颈就会衍生为严重的性能问题。
This problem can be avoided by placing the ID generation in its own transaction, so that the row is locked for as short a time as possible. But if the operations before and after the ID generation must all be in the same transaction, breaking up the operations into three separate transactions (before ID generation, ID generation, and after ID generation) is not an option.
只要把 ID 的生成操作放到一个独立的事务中完成,这个问题就不复存在了,这样行锁的持续时间就会变得尽可能得短了。但如果生成 ID 之前与之后的数据库操作都必须放在同一个事务中完成的话,那把这一整串的数据库操作切分为三个独立的事务(生成 ID 之前的数据库操作在事务 A 中完成,ID 生成操作在事务 B 中完成,生成 ID 后的数据操作在事务 C 中完成)去完成也并非是可选方案。
The solution is to use the JTA’s mechanism for suspending and resuming transactions. Suspending a transaction dissociates the transaction from its thread, leaving the thread without a current transaction. The transaction still exists and keeps track of the resources it has used so far, but any further work done by the thread does not use that transaction.
解决方案是使用 JTA 的事务悬停(suspending transactions)和重启机制(resuming transactions)。悬停一个事务,也即将事务与它关联的线程暂时分离开来,让线程暂时处于一个未关联任何事物的状态。而被剥离下来的事务依然存在,并且依旧追踪着它所使用到的资源,但是线程后续的操作将不再使用该事务。
After the transaction is suspended, the thread can create a transaction. Any further work done by the thread, such as the generation of an ID, occurs in that new transaction.
当原有的事务从线程中被剥离而处于悬停状态,线程就可以创建一个新的事务。该线程后续的任何数据库操作,比如生成一个 ID,就都在新的事务中执行了。
The new transaction can end after the ID has been generated, thereby committing the changes made to the ID counter. After ending this transaction, the thread again has no current transaction. The previously suspended transaction can now be resumed, which means that the transaction is re-associated with the original thread. The request can then continue using the same transaction and resources that it was using before the ID generator was used.
新的事务可以在生成完 ID 之后就结束,也即 ID 计数器的值变动被正常提交。当这个新事务被提交后,线程又再次变成了未绑定事务的状态。此时先前被悬停的事务就可以被重启了,也即该事务被重新关联到当前线程上来。这样,当前请求就可以继续使用 ID 生成操作之前所被使用的那个事务以及相应的资源了。
The steps are as follows:
* Suspend the current transaction before the ID generation.
* Create a transaction to handle the ID generation.
* End that transaction immediately after the ID generation
* Resume the suspended transaction.
大致步骤如下:
* 在 ID 生成操作前,悬停当前的事务
* 创建一个新的事务来单独执行 ID 生成操作
* 完成 ID 生成操作后立即结束这个新的事务
* 重启先前被悬停的事务
An application server can suspend and resume transactions through calls to the TransactionManager object; individual applications should not perform these operations directly. Instead, applications should use J2EE transaction demarcation facilities (described in the next section), and let the application server manage the underlying mechanics.
一个应用服务器可以通过调用 TransactionManager 对象来悬停与重启事务;独立部署的应用无法直接执行这些操作。而应该使用 J2EE 事务界限划定工具(J2EE transaction demarcation facilities 下一节会细说),让应用服务器来管理底层机制。