关于java:使用JPA / Toplink的批量插入

关于java:使用JPA / Toplink的批量插入

Batch insert using JPA/Toplink

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

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

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

此消息应按以下方式处理:

  • 从数据库中找到源和目标都匹配的User对象
  • 创建对象树:一条消息,其中包含消息文本的字段以及两个用于源和目标的用户对象
  • 将这棵树持久保存到数据库中。

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

我将Oracle用作备份数据库,将JPA与Toplink一起用于数据库处理任务。如果可能的话,我会留下来。

在没有太多优化的情况下,我可以在环境中实现约30个请求/秒的吞吐量。数量不多,我需要约300个请求/秒。因此,我测量了性能瓶颈所在的位置,并发现对em.persist()的调用花费了大部分时间。如果我简单地注释掉那条线,吞吐量将超过1000个请求/秒。

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

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

您知道一种使用JPA和Toplink创建批量插入的方法吗?您可以推荐其他任何方法来提高JPA持久性吗?

附加信息:

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

我试图通过在servlet内容和持久性之间创建一个内存中队列来使对em.persist()的调用异步。它极大地提高了性能。但是,队列确实增长很快,并且由于应用程序将连续不断地接收?200个请求/秒,这对我来说不是一个可接受的解决方案。

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


您应该与JPA界面解耦,并使用裸露的TopLink API。您可能可以将要持久保存的对象插入到UnitOfWork中,并按计划(同步或异步)提交UnitOfWork。注意,em.persist()的代价之一是整个对象图的隐式克隆。如果您自己uow.registerObject()两个用户对象,则TopLink会更好地工作,并保存自身必须进行的身份测试。因此,您将得到:

1
2
3
4
5
6
7
8
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中编写对象实际上是从数据库中读取序列信息,尤其是对于较小的默认值(我的序列大小可能有几百甚至更多)。


您对"请求/秒"的度量是多少?换句话说,对于第31个请求会发生什么?哪些资源被阻止?如果它是前端/ servlet / Web部分,您可以在另一个线程中运行em.persist()并立即返回吗?

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


推荐阅读