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

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

该请求包含发送者的 ID、接收者的 ID 和消息文本。

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

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

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

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

无需太多优化,我就可以在我的环境中实现约 30 个请求/秒的吞吐量。这并不多,我需要每秒约 300 个请求。所以我测量了性能瓶颈在哪里,发现调用 em.persist() 花费大部分时间。如果我简单地注释掉该行,吞吐量将远远超过 1000 个请求/秒。

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

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

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

附加信息:

“请求/秒”在这里意味着:请求总数/从测试开始到最后一条记录写入数据库的总时间。

我试着打电话给 em.persist() 通过在 servlet 内容和持久器之间创建内存队列来实现异步。对表演有很大帮助。然而,队列确实增长得非常快,并且由于应用程序将连续接收约 200 个请求/秒,这对我来说不是一个可接受的解决方案。

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

有帮助吗?

解决方案

您应该与 JPA 接口解耦并使用裸露的 TopLink API。您可以将要保留的对象放入工作单元中,并按您的计划提交工作单元(同步或异步)。请注意,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 ;)

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

其他需要寻找的东西:您的测序大小。在 TopLink 中编写对象所花费的大量时间实际上是从数据库中读取排序信息,尤其是默认值较小的情况(我的序列大小可能有数百甚至更多)。

其他提示

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

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

许可以下: CC-BY-SA归因
不隶属于 StackOverflow
scroll top