高性能休眠插入

问题描述 投票:15回答:3

我正在处理应用程序的延迟敏感部分,基本上我将收到一个网络事件转换数据,然后将所有数据插入到数据库中。在分析后,我发现基本上所有的时间都花在尝试保存数据上。这是代码

private void insertAllData(Collection<Data> dataItems)
{
    long start_time = System.currentTimeMillis();
    long save_time = 0;
    long commit_time = 0;
    Transaction tx = null;
    try
    {
        Session s = HibernateSessionFactory.getSession();
        s.setCacheMode(CacheMode.IGNORE);
        s.setFlushMode(FlushMode.NEVER);
        tx = s.beginTransaction();
        for(Data data : dataItems)
        {
            s.saveOrUpdate(data);
        }
        save_time = System.currentTimeMillis();
        tx.commit();
        s.flush();
        s.clear();
    }
    catch(HibernateException ex)
    {
        if(tx != null)
            tx.rollback();
    }
    commit_time = System.currentTimeMillis();
    System.out.println("Save: " + (save_time - start_time));
    System.out.println("Commit: " + (commit_time - save_time));
    System.out.println();
}

集合的大小总是小于20.这是我看到的时间数据:

Save: 27
Commit: 9

Save: 27
Commit: 9

Save: 26
Commit: 9

Save: 36
Commit: 9

Save: 44
Commit: 0

这对我来说很困惑。我认为save应该很快,所有的时间都应该花在commit上。但显然我错了。我也尝试删除事务(它不是真的必要)但我看到更糟糕的时候...我已经设置了hibernate.jdbc.batch_size = 20 ...

我可以期望获得多达500条消息/秒,所以我需要单个消息处理不到20毫秒。

我需要这个操作尽可能快,理想情况下只有一次往返数据库。我怎样才能做到这一点?

java performance hibernate3
3个回答
14
投票

将主键生成从服务器端自动递增移开。您的Java代码必须负责PK生成以避免往返。

对于体面的批量插入性能,您需要一种方法,在每次调用saveOrUpdate时都不需要访问数据库。使用UUID作为主键,或implementing HiLo可以帮助实现这一目标。否则,实际上没有批量插入。

为了兼顾其他外部系统的性能和互操作性,池化或pooled-lo优化器是最佳选择。


3
投票

老实说,我不知道你的测试和你所展示的“措施”可以合理地得出什么结论(我怀疑热身的开销很大,收集非常小,而且样本非常小)。

无论如何,我可以告诉你,你当前的代码不会扩展,你很可能在传递更大的集合时爆炸会话。您需要定期刷新和清除会话(如果批次大小为20,则每20条记录)。

实际上,我建议阅读整个Chapter 13. Batch processing


0
投票

一些基本的东西:

  • 您是否有没有索引的触发器或外键约束?
  • 你有配料司机吗?
  • 您的驱动程序是否处于批处理模式(请参阅Pascal参考中的hibernate.jdbc.batch_size)?
  • 你的表上的任何索引(如果你有很多索引,它有时会减慢插入)?

批处理是JDBC 2.0的一部分,它允许您以“批处理”执行多个语句;我们的想法是减少往返延迟(您可以为每个事务执行多个批处理)。

Statement stmt = dbCon.createStatement("insert into DataTable values (?,?,?)");
stmt.setInt(1, x1); stmt.setInt(2, x2), stmt.setString(3, "some value");
stmt.addBatch();
...
stmt.setInt(1, x2); stmt.setInt(2, x3), stmt.setString(3, "some other value");
stmt.addBatch();

stmt.executeBatch();
dbCon.commit();

您可以将其用作基准测试。我还会看看hibernate生成的SQL,看看它是否每次执行插件执行查询以获取生成的ID。

© www.soinside.com 2019 - 2024. All rights reserved.