问题

在Java中使用mongodb的MongoTemplate进行分页时,一般的策略是使用skip+limit的方式,但是这种方式在需要略过大量数据的时候就显得很低效(即skip趋向于变大)。

而且, 在数据查询语句中使用到了SORT排序的时候,mongo很容易内存溢出,报出如下错误:

lan executor error during find: Overflow sort stage buffered data usage of 33568573 bytes exceeds internal limit of 33554432 bytes

sort会将当前搜索出来的数据放入缓存,进行排序,如果一次性查询的输出量太大,超过33554432bytes,即报内存溢出异常

这种情况下要么去掉sort,要么减少一次性查询的输出量。

但是,sort是业务需求,能做的优化就是从一次性查询的输出量上想办法。

方法

目前我实践的有两种分页方法:

一、将上一页最后一条数据的时间戳作为搜索条件。这样做有一个局限,只能在连续跳页中使用,如果是间断式跳页,就无法满足。

二、如果skip略过量超过5000条,将搜索结果按5000条进行第一层分页,每次获取5000条数据中的最后一条数据的时间戳,用作下一次的查询条件,每次循环skip略过量就减少5000,循环直至skip少于5000条,然后再skip,代码如下:

int pageSkip = 5000;

if(s.getOffset()>pageSkip){

int offset = s.getOffset();

int cycleCount = s.getOffset()/pageSkip;

//每次查询5000条数据

long lastUpdateTime = 0;

while(cycleCount>0){

//分段查询

Query queryNew = getDBComponent().getQueryBuilder(s.getClass()).buildQuery(s);

queryNew.addCriteria(Criteria.where("dr").is(BaseObject.DR_YES));//dr 软删除下可见的数据

queryNew.with(new Sort(Sort.Direction.DESC,"lastUpdateTime"));

queryNew.skip(pageSkip);

if(lastUpdateTime!=0){

queryNew.addCriteria(Criteria.where("lastUpdateTime").lt(lastUpdateTime));

}

BaseObject baseObject = (BaseObject) getDBComponent().getMongoDao().findOne(queryNew);

lastUpdateTime = baseObject.getLastUpdateTime();

cycleCount--;

offset-=pageSkip;

}

query.skip(offset);

query.addCriteria(Criteria.where("lastUpdateTime").lt(lastUpdateTime));

}else{

query.skip(s.getOffset());

}

Logo

魔乐社区(Modelers.cn) 是一个中立、公益的人工智能社区,提供人工智能工具、模型、数据的托管、展示与应用协同服务,为人工智能开发及爱好者搭建开放的学习交流平台。社区通过理事会方式运作,由全产业链共同建设、共同运营、共同享有,推动国产AI生态繁荣发展。

更多推荐