2010-08-30 144 views
1

我有以下的使用情况下,我接收通过JMS消息有关的实体,通过它(不PK)的唯一性,它要求我更新的实体的状态:提交hibernate事务有多昂贵?

HibernateUtil.beginSession(); 
HibernateUtil.beginTransaction(); 
try{ 
    Entity entity = dao.getEntityByUniqueProperty(propertyValue); 
    if (entity==null){ 
    entity = dao.addEntityByUniqueProperty(propertyValue) 
    } 
    entity.setSomeProperty(otherPropertyValue); 
    HibernateUtil.commitTransaction(); 
} catch (ConstraintViolationException e){ 
    HibernateUtil.rollbackTransaction(); 
    //Do other things additionally 
} catch (StaleStateObjectException e){ 
    HibernateUtil.rollbackTransaction(); 
    //Do other things additionally 
} finally { 
    HibernateUtil.closeSession(); 
} 

在这个用例中,我必须准备好以下事实,即我尝试更新的实体尚未创建,因此我要求创建此实体(它的模板与独特属性精确一致),并且然后我改变它。 我的dillema如下: 一方面我有两个明显不同的块,我应该在适当的地方使用不同的catch子句,但是看到最后一种情况,当我查询时实体不在那里,但是在稍后有ms时我尝试创建它(因此ConstraintViolationException)是不应该经常发生并插入的,因为在中间的额外提交/ beginTransaction看起来似乎很糟糕。

我主要关心会话同步和JDBC连接在执行/开始时发生的额外性能影响。
我错了吗?我在哪里找不到优化?我错过了什么吗?
在此先感谢

回答

1

我想我居然发现我使用的具体问题情况下,这是只打开事务实际上需要的时候,所以我保存过早性能dillema:

try { 
    HibernateUtil.beginSession(); 
    Entity entity = dao.getEntityByUniqueProperty(propertyValue); 
    if (entity==null){ 
     HibernateUtil.beginTransaction(); 
     try { 
      entity = dao.addEntityByUniqueProperty(propertyValue) 
      HibernateUtil.commitTransaction(); 
     } catch (ConstraintViolationException e){ 
      HibernateUtil.rollbackTransaction(); 
      HibernateUtil.closeSession(); 
      HibernateUtil.beginSession(); 
      entity = dao.getEntityByUniqueProperty(propertyValue); 
      //Do other things additionally 
     } 
    } 
    entity.setSomeProperty(otherPropertyValue); 
    HibernateUtil.commitTransaction(); 
} catch (StaleStateObjectException e){ 
    HibernateUtil.rollbackTransaction(); 
    //Do other things additionally 
} finally { 
    HibernateUtil.closeSession(); 
} 

这将使我本地化的具体在每次交易中都存在风险,并且在不需要时避免提交和开启交易。 感谢您的意见。

+0

我不会建议,最好的做法是总是有一个交易,即使是选择,见亚伦的答案。另外你会发现JDBC驱动程序可能会发出一个事务调用。老实说,除非你试图做数百秒,这不可能是一个问题。我将编辑我的答案,我将如何解决这个问题。 – 2010-08-30 17:26:05

+1

@Mike这是不正确的,不使用事务进行只读查询是非常好的。 – 2010-08-30 19:14:17

3

首先,您需要一个事务。没有它,上面的代码将无法正常工作,因为这可能发生:

  • 线程1创建唯一的实例
  • 线程2获得唯一实例
  • 线程2套其他财产
  • 线程1套其他财产
  • 线程1刷新高速缓存
  • 线程2次刷新缓存

问题:数据库在这种情况下是否一致?在类似的情况下会不会一致?

始终使用交易。数据库针对它进行了优化。如果你有问题,开始考虑你的设计。就像每秒处理数千条消息时一样您的性能工具显示此代码已成为瓶颈。不要相信你的直觉。

+0

除非我误解了OP是问txn是多么昂贵,因为他正在考虑引发ConstraintViolationException的情况,他必须处理使用单独/新事务处理角落案例。而不是是否使用txns。 – 2010-08-30 15:28:20

+0

@MikeQ:你说的对,但这不是问题。他可能会获得更好的表现,但他会同时损坏他的数据。 – 2010-08-30 15:51:23

2

hibernate事务几乎只是一个数据库事务的包装。因此,数据库交易的成本很高。

与往常一样,优化通常最好有清晰安全的代码,试图消除额外的1%性能。但我不知道你的用例。如果以上每秒钟被调用几次,那么不要担心性能。如果它每秒被召唤几百次,那么这可能是一个问题。

如果您遇到性能问题,请测量/时间/剖析代码,直至找到问题。通常情况下,你可以假设问题在某个地方,而实际上是在其他地方。

在你的情况上面我会做以下

  • 将while循环圆你的代码(这一切,含会议开/关)
  • 如果它击中ConstraintViolationException块日志和continue所以不是编码一些额外的逻辑只是让它再试一次,它会找到另一个事务添加并适当更新的新行。
  • 它,它的工作原理确定,然后break圈外

编辑:我将如何做到这一点....

// loop as it is possible we get a retryable exception, see below 
while (true){ 
    HibernateUtil.beginSession(); 
    HibernateUtil.beginTransaction(); 
    try{ 
     Entity entity = dao.getEntityByUniqueProperty(propertyValue); 
     if (entity==null){ 
     entity = dao.addEntityByUniqueProperty(propertyValue) 
     } 
     entity.setSomeProperty(otherPropertyValue); 
     HibernateUtil.commitTransaction(); 

     // success 
     break; 

    } catch (ConstraintViolationException e){ 
     HibernateUtil.rollbackTransaction(); 

     // this is ok, another txn must have added the entity at the same time 
     // try again and it will find the entity this time 
     continue; 

    } catch (StaleStateObjectException e){ 
     HibernateUtil.rollbackTransaction(); 
     //Do other things additionally 

    } finally { 
     HibernateUtil.closeSession(); 
    } 
} 
+0

+1:“...通常情况下,您可以假设问题在某个地方,而实际上是在其他地方。”确实如此。 关于“一个hibernate事务几乎只是一个数据库事务的包装器的事实,所以这个数据库事务的成本很高。”数据库事务有多昂贵?大致?谢谢 – Ittai 2010-08-30 17:12:35

+0

您大概可以每秒执行几百次小型数据库事务。棒球场。 – 2010-08-30 17:32:15

1

无论你做什么,写操作都不能在事务之外完成,如果没有正在进行的事务并抛出异常,Hibernate会投诉。所以这里没有选择。

现在,您的使用案例 - 一个findOrCreate() - 不是一件小事。至于你提到,你可以面对的竞争条件:

 
T1: BEGIN TX; 

T2: BEGIN TX; 

T1: getEntityByUniqueProperty("foo"); //returns null 

T1: getEntityByUniqueProperty("foo"); //returns null 

T1: addEntityByUniqueProperty("foo"); 
T1: COMMIT; //row inserted 

T1: addEntityByUniqueProperty("foo"); 
T2: COMMIT; //constraint violation 

所以你必须要么

  1. 同步的代码(仅如果您在单个JVM上运行的选项)〜或〜
  2. 锁定整个表格(ouch!)〜或〜
  3. 处理它并执行某种重试机制。

就个人而言,我会选择3.性能方面,它是最好的选择,实际上它是一种常见模式,特别是在使用消息传递和高并发性时。