Spring 交易会减慢整个过程
Spring Transactional slows down complete process
我正在尝试分析我有两个 classes 的情况。一个 class 是 ProcessImpl,它是起点并在内部调用其他 child transactions.I 不知道出了什么问题。
processImpl 正在导入一些东西并将相关数据写入数据库。
Specs
Spring-orm 版本:3.2.18.RELEASE。
JDK 版本:1.8。
Db : H2(在任何数据库上都记录了相同的性能)。
Issue
如果我从 ProcessImpl.processStage()
中删除 @Transactional
,该过程需要 ~ 50 秒
如果我将 @Transactional
与 ProcessImpl.processStage()
保持一致,则该过程需要 ~ 15 分钟 。
不知道为什么会这样。
长期以来,我一直在努力解决这个问题,但没有运气。请看下面的代码。
要求:
完整的 processStage()
应该完成或完全回滚,即使 child 事务之一失败。
仅供参考:我也收到很多消息,例如:"Participating in existing transaction"。试图通过将 propagation=Propagation.NESTED
添加到 processStage()
来解决这个问题,但没有成功。
ProcessImpl Class.
public class ProcessImpl {
/*This is the big transaction that calls other transactional stuff from MyServiceImpl
* This is starting point you can say for the process...
*
* If we remove @Transactional from here the process is lightning fast
* With transactional : 15minutes
* Without transactional : 50 seconds
* */
@Transactional
public void processStage(){
MyServiceImpl mp = new MyServiceImpl();
//do some stuff
mp.doWork1();
//do more work
mp.doWork2();
}
}
MyServiceImpl Class
class MyServiceImpl{
@Transactional
public void doWork1(){
Object o = doChildWork();
// and more stuff
//calls other class services and dao layers
}
@Transactional
public void doWork2(){
//some stuff
doChildWork2();
doChildWork();
//more work
}
@Transactional
public Object doChildWork(){
return new Object(); //hypothetical, I am returning list and other collection stuff
}
@Transactional
public Object doChildWork2(){
return new Object(); //hypothetical, I am returning list and other collection stuff
}
}
此外,这里会出现自调用问题,这在 Transactional 中是不可取的吗?
很难猜测您的代码中到底发生了什么,但可能存在以下问题:
锁定数据库级别。
当您在 doWork1()
和 doWork2()
中更新相同的数据库对象时,可能会发生这种情况。由于这两种方法都是在一个事务中执行的,因此在 doWork1()
内完成的更新将不会提交,直到 doWork2()
完成。这两种方法都可能尝试锁定同一个数据库对象并等待它。从技术上讲,它可以是任何数据库对象:table 中的行、索引、整个 table 等
分析您的代码并尝试找出可以锁定的内容。您还可以在方法为 运行 时查看数据库事务日志。所有流行的数据库都提供有助于查找有问题的地方的功能。
在 Hibernate 上下文刷新期间放慢速度。 以防更新太多对象时 ORM 引擎(比方说 Hibernate)必须接收它们并将它们保存在内存中。从字面上看,Hibernate 必须具有更新对象的所有旧状态和所有新状态。有时它不是以最佳方式执行此操作。
您可以使用调试来指出这一点。尝试找到最慢的地方并检查那里到底调用了什么。我可能猜想它会在休眠更新缓存状态时变慢。
还有一个问题。 我看到您在 processStage()
期间使用构造函数创建了 MyServiceImpl
。我建议您将此代码替换为 spring 自动装配。首先,您使用它的方式不是它设计的使用方式,但理论上这也会以某种方式影响执行。
will I get self invocation issue, which is not advisable in Transactional?
不,忽略所有注释也能正常工作。在 doWork2()
中调用 doChildWork()
和 doChildWork2()
将被视为标准 java 调用(spring 无法向它们添加任何“魔法”,只要你直接调用它们)。
这里的任何答案实际上都只是(消息灵通的)推测。在这种情况下,最好的办法是使用 Java 分析器并进行详细的 cpu 级别分析以准确计算出发生了什么。
我推荐优秀的 YourKit,它是商业的,但你可以免费试用。
https://www.yourkit.com/docs/java/help/performance_bottlenecks.jsp
我正在尝试分析我有两个 classes 的情况。一个 class 是 ProcessImpl,它是起点并在内部调用其他 child transactions.I 不知道出了什么问题。 processImpl 正在导入一些东西并将相关数据写入数据库。
Specs
Spring-orm 版本:3.2.18.RELEASE。
JDK 版本:1.8。
Db : H2(在任何数据库上都记录了相同的性能)。
Issue
如果我从 ProcessImpl.processStage()
中删除 @Transactional
,该过程需要 ~ 50 秒
如果我将 @Transactional
与 ProcessImpl.processStage()
保持一致,则该过程需要 ~ 15 分钟 。
不知道为什么会这样。
长期以来,我一直在努力解决这个问题,但没有运气。请看下面的代码。
要求:
完整的 processStage()
应该完成或完全回滚,即使 child 事务之一失败。
仅供参考:我也收到很多消息,例如:"Participating in existing transaction"。试图通过将 propagation=Propagation.NESTED
添加到 processStage()
来解决这个问题,但没有成功。
ProcessImpl Class.
public class ProcessImpl {
/*This is the big transaction that calls other transactional stuff from MyServiceImpl
* This is starting point you can say for the process...
*
* If we remove @Transactional from here the process is lightning fast
* With transactional : 15minutes
* Without transactional : 50 seconds
* */
@Transactional
public void processStage(){
MyServiceImpl mp = new MyServiceImpl();
//do some stuff
mp.doWork1();
//do more work
mp.doWork2();
}
}
MyServiceImpl Class
class MyServiceImpl{
@Transactional
public void doWork1(){
Object o = doChildWork();
// and more stuff
//calls other class services and dao layers
}
@Transactional
public void doWork2(){
//some stuff
doChildWork2();
doChildWork();
//more work
}
@Transactional
public Object doChildWork(){
return new Object(); //hypothetical, I am returning list and other collection stuff
}
@Transactional
public Object doChildWork2(){
return new Object(); //hypothetical, I am returning list and other collection stuff
}
}
此外,这里会出现自调用问题,这在 Transactional 中是不可取的吗?
很难猜测您的代码中到底发生了什么,但可能存在以下问题:
锁定数据库级别。
当您在 doWork1()
和 doWork2()
中更新相同的数据库对象时,可能会发生这种情况。由于这两种方法都是在一个事务中执行的,因此在 doWork1()
内完成的更新将不会提交,直到 doWork2()
完成。这两种方法都可能尝试锁定同一个数据库对象并等待它。从技术上讲,它可以是任何数据库对象:table 中的行、索引、整个 table 等
分析您的代码并尝试找出可以锁定的内容。您还可以在方法为 运行 时查看数据库事务日志。所有流行的数据库都提供有助于查找有问题的地方的功能。
在 Hibernate 上下文刷新期间放慢速度。 以防更新太多对象时 ORM 引擎(比方说 Hibernate)必须接收它们并将它们保存在内存中。从字面上看,Hibernate 必须具有更新对象的所有旧状态和所有新状态。有时它不是以最佳方式执行此操作。
您可以使用调试来指出这一点。尝试找到最慢的地方并检查那里到底调用了什么。我可能猜想它会在休眠更新缓存状态时变慢。
还有一个问题。 我看到您在 processStage()
期间使用构造函数创建了 MyServiceImpl
。我建议您将此代码替换为 spring 自动装配。首先,您使用它的方式不是它设计的使用方式,但理论上这也会以某种方式影响执行。
will I get self invocation issue, which is not advisable in Transactional?
不,忽略所有注释也能正常工作。在 doWork2()
中调用 doChildWork()
和 doChildWork2()
将被视为标准 java 调用(spring 无法向它们添加任何“魔法”,只要你直接调用它们)。
这里的任何答案实际上都只是(消息灵通的)推测。在这种情况下,最好的办法是使用 Java 分析器并进行详细的 cpu 级别分析以准确计算出发生了什么。
我推荐优秀的 YourKit,它是商业的,但你可以免费试用。
https://www.yourkit.com/docs/java/help/performance_bottlenecks.jsp