【问题标题】:How can I retrieve long response from mongodb?如何从 mongodb 检索长响应?
【发布时间】:2014-10-15 09:01:29
【问题描述】:

在 Spring mvc + mongodb 应用程序中,我有 400k 个文档。如果在进行查询时需要返回 300k 文档,我该怎么做?

下面是堆栈跟踪,

HTTP Status 500 - Request processing failed; nested exception is java.lang.IllegalArgumentException: response too long: 1634887426

type Exception report

message Request processing failed; nested exception is java.lang.IllegalArgumentException: response too long: 1634887426

description The server encountered an internal error that prevented it from fulfilling this request.

exception

org.springframework.web.util.NestedServletException: Request processing failed; nested exception is java.lang.IllegalArgumentException: response too long: 1634887426
    org.springframework.web.servlet.FrameworkServlet.processRequest(FrameworkServlet.java:973)
    org.springframework.web.servlet.FrameworkServlet.doPost(FrameworkServlet.java:863)
    javax.servlet.http.HttpServlet.service(HttpServlet.java:646)
    org.springframework.web.servlet.FrameworkServlet.service(FrameworkServlet.java:837)
    javax.servlet.http.HttpServlet.service(HttpServlet.java:727)
root cause

java.lang.IllegalArgumentException: response too long: 1634887426
    com.mongodb.Response.<init>(Response.java:49)
    com.mongodb.DBPort$1.execute(DBPort.java:141)
    com.mongodb.DBPort$1.execute(DBPort.java:135)
    com.mongodb.DBPort.doOperation(DBPort.java:164)
    com.mongodb.DBPort.call(DBPort.java:135)
    com.mongodb.DBTCPConnector.innerCall(DBTCPConnector.java:292)
    com.mongodb.DBTCPConnector.call(DBTCPConnector.java:271)
    com.mongodb.DBCollectionImpl.find(DBCollectionImpl.java:84)
    com.mongodb.DBCollectionImpl.find(DBCollectionImpl.java:66)
    com.mongodb.DBCursor._check(DBCursor.java:458)
    com.mongodb.DBCursor._hasNext(DBCursor.java:546)
    com.mongodb.DBCursor.hasNext(DBCursor.java:571)
    org.springframework.data.mongodb.core.MongoTemplate.executeFindMultiInternal(MongoTemplate.java:1803)
    org.springframework.data.mongodb.core.MongoTemplate.doFind(MongoTemplate.java:1628)
    org.springframework.data.mongodb.core.MongoTemplate.doFind(MongoTemplate.java:1611)
    org.springframework.data.mongodb.core.MongoTemplate.find(MongoTemplate.java:535)
    com.AnnaUnivResults.www.service.ResultService.getStudentList(ResultService.java:38)
    com.AnnaUnivResults.www.service.ResultService$$FastClassBySpringCGLIB$$1f19973d.invoke(<generated>)
    org.springframework.cglib.proxy.MethodProxy.invoke(MethodProxy.java:204)
    org.springframework.aop.framework.CglibAopProxy$CglibMethodInvocation.invokeJoinpoint(CglibAopProxy.java:711)
    org.springframework.aop.framework.ReflectiveMethodInvocation.proceed(ReflectiveMethodInvocation.java:157)
    org.springframework.dao.support.PersistenceExceptionTranslationInterceptor.invoke(PersistenceExceptionTranslationInterceptor.java:136)
    org.springframework.aop.framework.ReflectiveMethodInvocation.proceed(ReflectiveMethodInvocation.java:179)
    org.springframework.aop.framework.CglibAopProxy$DynamicAdvisedInterceptor.intercept(CglibAopProxy.java:644)
    com.AnnaUnivResults.www.service.ResultService$$EnhancerBySpringCGLIB$$f9296292.getStudentList(<generated>)
    com.AnnaUnivResults.www.controller.ResultController.searchStudentByCollOrDept(ResultController.java:87)
    sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
    sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:39)
    sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:25)
    java.lang.reflect.Method.invoke(Method.java:597)
    org.springframework.web.method.support.InvocableHandlerMethod.invoke(InvocableHandlerMethod.java:215)
    org.springframework.web.method.support.InvocableHandlerMethod.invokeForRequest(InvocableHandlerMethod.java:132)
    org.springframework.web.servlet.mvc.method.annotation.ServletInvocableHandlerMethod.invokeAndHandle(ServletInvocableHandlerMethod.java:104)
    org.springframework.web.servlet.mvc.method.annotation.RequestMappingHandlerAdapter.invokeHandleMethod(RequestMappingHandlerAdapter.java:749)
    org.springframework.web.servlet.mvc.method.annotation.RequestMappingHandlerAdapter.handleInternal(RequestMappingHandlerAdapter.java:690)
    org.springframework.web.servlet.mvc.method.AbstractHandlerMethodAdapter.handle(AbstractHandlerMethodAdapter.java:83)
    org.springframework.web.servlet.DispatcherServlet.doDispatch(DispatcherServlet.java:945)
    org.springframework.web.servlet.DispatcherServlet.doService(DispatcherServlet.java:876)
    org.springframework.web.servlet.FrameworkServlet.processRequest(FrameworkServlet.java:961)
    org.springframework.web.servlet.FrameworkServlet.doPost(FrameworkServlet.java:863)
    javax.servlet.http.HttpServlet.service(HttpServlet.java:646)
    org.springframework.web.servlet.FrameworkServlet.service(FrameworkServlet.java:837)
    javax.servlet.http.HttpServlet.service(HttpServlet.java:727)

我猜上面的堆栈跟踪是因为返回的文档非常大。我该如何管理?我将tomcat服务器配置更改为4096M。但我还是有问题。

【问题讨论】:

    标签: java spring mongodb spring-mvc spring-data-mongodb


    【解决方案1】:

    数学第一:您尝试在单个查询中加载超过 1.5GB 的数据。这将需要一段时间,除了 非常 罕见的用例显示 - 抱歉 - 糟糕的应用程序设计。

    不要从数据库方面考虑如何处理。你应该重构你的代码。

    加载大量文档有两种常见情况。

    场景 1:对结果集进行计算

    有时您想对大部分结果集进行计算。假设您想了解到目前为止所有来自 EMEA 的客户产生了多少营业额,并且您的订单文件如下所示(为了本示例而进行了简化):

    {
     _id:<...>,
     customerId:<...>,
     deliveryAdress: {<...>},
     region: "EMEA",
     items:[{<...>},{<...>},...],
     total:12345.78
    }
    

    现在,您可以在某种程度上做的是加载来自 EMEA 地区的所有订单,相当于

    db.orders.find({region:"EMEA"})
    // the repository method would be something like
    // findByRegion(String region)
    

    并遍历结果集,构建total 的总和。这种方法有几个问题。首先,即使以这种方式进行,您也会加载很多不需要的数据(items,deliveryAddress)。所以减少MongoDB返回数据量的第一种方法就是使用投影:

    db.orders.find({region:"EMEA"},{_id:0,total:1})
    // as of now, you would have to create a custom method
    // and a custom repository implementation
    // See "Further Reading"
    

    这将为您提供大量文档,其中仅包含来自 EMEA 的所有订单的总数,从而大大减少了从数据库返回的大小。据我所知,这不能自动使用 spring-data 的动态查找器(存储库)来完成。

    但这种方法仍然存在无法很好扩展的缺点,因为在某个时间点,您从 EMEA 获得的订单可能比您在单个交易中加载的要多。您可以使用服务器端游标和迭代器(详见场景 2),但这仍然有点尴尬。

    更好的方法是让 MongoDB 进行计算。为此,您将使用 MongoDB 的聚合框架。至于这个例子,查询看起来像

    db.orders.aggregate([{$match:{region:"EMEA"}},{$group:{_id:"$region",totalTurnover:{$sum:"$total"} } })
    

    这将返回一个看起来像的单个文档

    {_id:"EMEA",totalTurnover:<very large Sum>}
    

    优势很明显:您保持应用程序的负载,无需加载所有数据,大大提高了性能。而且它是可扩展的。

    场景 2:您真的需要大量的文档

    即使您确实需要大量文档,将它们全部加载到一个巨大的结果集中也是一种不好的做法,因为您发现这种方法不可扩展。更好的方法是请求部分结果集。为此,您使用服务器端游标。

    使用 spring-data-mongodb 您将使用 PagingAndSortingRepository 而不是 CrudRepository 或任何其他。由于 PagingAndSortingRepository 是 CrudRepository 的扩展,迁移应该很容易。优点是您只在给定时间点请求结果集的一部分,这使得您的查询可扩展,但需要手动迭代它。

    进一步阅读

    【讨论】:

      猜你喜欢
      • 2023-03-15
      • 1970-01-01
      • 1970-01-01
      • 2014-07-12
      • 2010-11-25
      • 1970-01-01
      • 2020-01-14
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多