使用JPA / Toplink批量插入

时间:2008-09-15 17:09:27

标签: java oracle jpa toplink

我有一个通过HTTP接口接收消息的Web应用程序,例如:

http://server/application?source=123&destination=234&text=hello

此请求包含发件人的ID,收件人的ID和邮件的文本。

此消息的处理方式如下:

  • 从数据库中找到源和目标的匹配用户对象
  • 创建对象树:包含消息文本字段的消息和源和目标的两个用户对象
  • 将此树持久保存到数据库中。

树将被其他我无法触摸的应用程序加载。

我使用Oracle作为后备数据库,使用JPA和Toplink进行数据库处理任务。如果可能的话,我会留下这些。

如果没有太多优化,我可以在我的环境中实现~30个请求/秒的吞吐量。那不算多,我需要~300个请求/秒。所以我测量了性能瓶颈的位置,发现em.persist()的调用占用了大部分时间。如果我只是注释掉那一行,那么吞吐量就会超过1000个请求/秒。

我尝试编写一个小型测试应用程序,它使用简单的JDBC调用将100万条消息持久保存到同一个数据库中。我使用批处理,这意味着我做了100次插入然后提交,并重复直到所有记录都在数据库中。在这种情况下,我测量了大约500个请求/秒的吞吐量,这将满足我的需求。

很明显,我需要在此优化插入性能。但是正如我之前提到的,我想继续使用JPA和Toplink,而不是纯JDBC。

您是否知道使用JPA和Toplink创建批量插入的方法?你能推荐任何其他技术来提高JPA持久性能吗?

附加信息:

“requests / sec”在这里表示:从测试开始到写入数据库的最后一条记录的请求总数/总时间。

我尝试通过在servlet内容和persister之间创建内存中队列来异步调用em.persist()。它大大有助于提高性能。然而,队列确实增长得非常快,并且应用程序将连续接收~200个请求/秒,这对我来说不是一个可接受的解决方案。

在这种解耦方法中,我收集了100毫秒的请求,并在提交交易之前对所有收集的项目调用了em.persist()。 EntityManagerFactory在每个事务之间缓存。

2 个答案:

答案 0 :(得分:3)

您应该从JPA界面解耦并使用裸TopLink API。您可以将您持久存在的对象放入UnitOfWork并按计划提交UnitOfWork(同步或异步)。请注意,em.persist()的成本之一是整个对象图中发生的隐式克隆。如果你自己uow.registerObject()你的两个用户对象,TopLink会更好地工作,保存自己的身份测试。所以你最终会得到:

uow=sess.acquireUnitOfWork();
for (job in batch) {
 thingyCl=uow.registerObject(new Thingy());
 user1Cl=uow.registerObject(user1);
 user2Cl=uow.registerObject(user2);
 thingyCl.setUsers(user1Cl,user2Cl);
}
uow.commit();

这是非常老派的TopLink btw;)

请注意,批处理将有很大帮助,因为批处理写入,尤其是带参数绑定的批处理写入将会起作用,对于这个简单的示例,可能会对您的性能产​​生非常大的影响。

要寻找的其他事项:您的测序大小。在TopLink中编写对象所花费的大部分时间实际上都花在从数据库中读取排序信息,特别是使用较小的默认值(我的序列大小可能会有几百甚至更多)。

答案 1 :(得分:0)

您对“请求/秒”的衡量标准是什么?换句话说,第31个请求会发生什么?什么资源被阻止了?如果它是前端/ servlet / web部分,你可以在另一个线程中运行em.persist()并立即返回吗?

另外,您每次都在创建交易吗?您是否在为每个请求创建EntityManagerFactory对象?