mongodb排序(聚合)超出内存限制
前情摘要#
在某次开发过程中,有一个页面查询前几页的数据都能够正常返回,但是当查询最后一页时发现后台抛出了异常,详细信息如下:
Caused by: com.mongodb.MongoQueryException: Query failed with error code 96 and error message 'Executor error during find command :: caused by :: errmsg: "Sort operation used more than the maximum 33554432 bytes of RAM. Add an index, or specify a smaller limit."' on server 192.168.0.130:27017
错误分析#
查看错误信息我们可以发现,more than the maximum 33554432 bytes of RAM,即排序操作超过了MongoDB单个Session排序可使用的最大内存限制。
官方文档给出的解释:
In MongoDB, sort operations can obtain the sort order by retrieving documents based on the ordering in an index. If the query planner cannot obtain the sort order from an index, it will sort the results in memory. Sort operations that use an index often have better performance than those that do not use an index. In addition, sort operations that do not use an index will abort when they use 32 megabytes of memory.
文档中意思大概是:在排序字段未利用到索引的情况下,若超过32M内存则会被Abort,语句直接返回报错。那么解决问题的方法很简单,在错误信息提也提示了, Add an index, or specify a smaller limit.,我们只需要给要排序的字段添加上索引或者把需要排序的数据限制的更小就行了。
但是,业务程序中查询一般都是使用聚合查询方法 aggregate(),对于聚合查询中的Sort Stage,官方文档说明了使用内存排序能使用最大的内存为 100M,要执行大型排序,需要启用allowDiskUse=true选项使用系统缓存将数据写入临时文件以进行排序。来避免报错。
官方文档见:https://docs.mongodb.com/manual/reference/operator/aggregation/sort/#sort-memory-limit
解决过程#
非聚合查询#
我们只需要给要排序的字段添加上索引或者把需要排序的数据限制的更小就行了。
Java聚合查询中对allowDiskUse设置方法:#
对于Spring Data MongoDB 1.5.x以上2.0版本以下版本并没有提供显式的封装 , 故而需要自己创建:
AggregationOptions aggregationOptions = new AggregationOptions(true, false, null);
2.0及以上版本提供内部静态类,以builder的方式设置:
AggregationOptions aggregationOptions = AggregationOptions.builder().allowDiskUse(true).build();
然后在Aggregation.newAggregation之后加上.withOptions(aggregationOptions)
// 对于Spring Data MongoDB 1.5.x以上2.0版本以下版本并没有提供显式的封装 , 故而需要自己创建:
AggregationOptions aggregationOptions = new AggregationOptions(true, false, null);
// 2.0及以上版本提供内部静态类,以builder的方式设置:
AggregationOptions aggregationOptions = AggregationOptions.builder().allowDiskUse(true).build();
Aggregation aggregation = Aggregation.newAggregation(
Aggregation.match(criteria),
Aggregation.sort(new Sort(new Sort.Order(direction, property))),
Aggregation.skip((long) page),
Aggregation.limit(size)
).withOptions(aggregationOptions); // 在此处运用配置
List<OrderItem> orderItems = mongoTemplate.aggregate(aggregation, "mro_orders_items", OrderItem.class).getMappedResults();
long count = mongoTemplate.aggregate(aggregation, "mro_orders_items", OrderItem.class).getMappedResults().size();
Mongo原生聚合函数对allowDiskUse设置方法#
在以JAVA代码方式解决后,又去琢磨了一下原生函数的处理方法
db.getCollection("mro_orders_items").aggregate([
{
$project: {
_id: "$orderId"
}
},
{
$skip: 0
},
{
$limit: 100
},
{
$sort: {
createAt: 1
}
}
], {
allowDiskUse: true
})
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· TypeScript + Deepseek 打造卜卦网站:技术与玄学的结合
· Manus的开源复刻OpenManus初探
· AI 智能体引爆开源社区「GitHub 热点速览」
· 从HTTP原因短语缺失研究HTTP/2和HTTP/3的设计差异
· 三行代码完成国际化适配,妙~啊~