高性能休眠插件

2022-09-03 06:27:34

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

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

这让我感到困惑。我认为应该很快,所有的时间都应该花在.但显然我错了。我也尝试过删除交易(它不是真的必要),但我看到了更糟糕的时代......我设置了hibernate.jdbc.batch_size=20...savecommit

我可以期望获得多达500条消息/秒,因此我需要单个消息处理小于20毫秒。

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


答案 1

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

为了获得不错的批量插入性能,您需要一种方法,该方法不需要在每次调用时都点击数据库来保存OrUpdate。使用UUID作为主键或实现HiLo可以帮助实现这一目标。否则,实际上不会进行批量插入。

为了同时具有性能并与其他外部系统具有互操作性,池化池化 lo 优化器是最佳选择。


答案 2

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

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

实际上,我建议阅读整个第13章。批处理


推荐