在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。但我还是有问题。
答案 0 :(得分:2)
首先是数学:你试图在一个查询中加载超过1.5GB。这将需要一段时间,除了非常罕见的用例显示 - 抱歉 - 糟糕的应用程序设计。
不要想到如何从数据库方面处理它。你应该重构你的代码。
加载大量文档有两种常见方案。
有时您希望对结果集的大部分进行计算。假设您想知道到目前为止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>}
优势显而易见:您保持应用程序的负载,不需要加载所有数据,从而大大提高性能。它具有可扩展性。
即使您确实需要大量文档,将它们全部加载到一个巨大的结果集中也是不好的做法,因为您发现这种方法不具备可扩展性。更好的方法是请求结果集的部分。为此,您使用服务器端游标。
使用spring-data-mongodb,你可以使用PagingAndSortingRepository而不是CrudRepository或其他任何东西。由于PagingAndSortingRepository是CrudRepository的扩展,因此迁移应该非常简单。优点是您只在给定时间点请求结果集的一部分,这使您的查询可以以手动迭代它为代价进行扩展。