代码之家  ›  专栏  ›  技术社区  ›  luke

高性能休眠插入

  •  15
  • luke  · 技术社区  · 16 年前

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

    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\u size=20。。。

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

    3 回复  |  直到 16 年前
        1
  •  17
  •   Vlad Mihalcea    8 年前

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

    为了获得良好的大容量插入性能,您需要一个方法,该方法不需要在每次调用saveOrUpdate时命中数据库。使用uuid作为主键,或者 implementing HiLo 可以帮助实现这一点。否则,就不会进行批量插入。

    为了同时具备与其他外部系统的性能和互操作性 或者 pooled-lo

        2
  •  3
  •   Pascal Thivent    16 年前

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

    不管怎样,我可以告诉你,你当前的代码无法扩展,当你传递一个更大的集合时,你很可能会破坏会话。您需要定期刷新和清除会话(如果批大小为20,则每20条记录)。

    实际上,我建议你通读全文 Chapter 13. Batch processing .

        3
  •  0
  •   Justin    16 年前

    一些基本的东西:

    • 没有索引的约束?
    • 你们有批处理驱动程序吗?
    • 驱动程序是否处于批处理模式(请参阅Pascal参考资料中的hibernate.jdbc.batch\u size)?
    • 表上有索引吗(如果索引太多,有时会减慢插入速度)?

    批处理是JDBC2.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。