解决mongodb大数据量分页查询效率问题

最常见的分页采用的是skip+limit这种组合方式,这种方式对付小数据倒也可以,但是对付上几百上千万的大数据,只能力不从心skip如果跳过大量的数据会很慢,并且会越查越慢,针对这一情况,可以通过条件查询+排序+限制返回记录,即 边查询,边排序,排序之后,抽取上一页中的最后一条记录,作为当前分页的查询条件,从而避免了skip效率低下的问题。

代码如下: 

 

/** 
 * 大数据量排序方式分页获取数据
* @param page 页码
* @param pageSize 每页条数
* @param collectionName 表名
* @param totalCon 查询条件汇总 ,在执行之前需要判断是否需要存放最后一条的ID【totalCon.put("_id", new BasicDBObject(QueryOperators.GT, lastStr))】,当_id是自动生成的时候,不要用转换后的_id
* @param fields 输出指定字段
*/ 
public DBCursor largePageList(int page, int pageSize, String collectionName, BasicDBObject totalCon, BasicDBObject fields) {
    	try {
    		return mongoTemplate.getCollection(collectionName).find(totalCon,fields).sort(new BasicDBObject("_id", 1)).limit(pageSize);
	} catch (Exception e) {
			LoggerUtil.error("***大数据量数据分页查询失败,collectionName=" + collectionName, e.getCause().getMessage());
	}
    return null;
}

调用代码示例:

	/**
	 * 执行方法
	 */
	public void exeMethod() {
		long start = System.currentTimeMillis(); // 记录起始时间
		int lastExeNum = 0;
		int pageSize = 500;//每次查询的记录数
		try {
			//拼装条件,30天<timestamp<7天
			BasicDBObject totalCon = new BasicDBObject();
			totalCon.put("timestamp", new BasicDBObject("$gte", 
					DateUtils.dateToStr(DateUtils.getComputeTime(new Date(),Calendar.DAY_OF_MONTH,longDay), DateUtils._yMdHms)).append("$lte",  
							DateUtils.dateToStr(DateUtils.getComputeTime(new Date(),Calendar.DAY_OF_MONTH,-7), DateUtils._yMdHms))); 
			totalCon.put("isFrom", Yes);
			
			//输出指定字段
			BasicDBObject fields = new BasicDBObject();
			fields.put("_id", 1);
			fields.put("timestamp", 1);
			
			DBCursor cursorless = null;
			Map<String, String> map = null;
			DBObject obj = null;
			
			for(int j=0;j<100;j++){
				cursorless = null;
				obj = null;
				map = null;
				TimeUnit.SECONDS.sleep(10);
				
				String lastStr ="";//上一页最后一条数据的 _id
				String table = String.format(DBKeys.jruseractive, j);
				if(totalCon.containsField("_id")){
					totalCon.removeField("_id");
				}
				long count= mongoDao.count(new BasicQuery(totalCon), table);
				int page = ((int)count + (pageSize - 1)) / pageSize;

				for(int i=1;i<=page;i++){
					cursorless = null;
					obj = null;
					map = null;
					TimeUnit.MILLISECONDS.sleep(10);
					
					//上一页最后一条数据,如果不为空,就加上,作为查询下一页的条件
					if (!"".equals(lastStr)) {
						totalCon.put("_id", new BasicDBObject(QueryOperators.GT, lastStr));  
					}
					cursorless = mongoDao.largePageList(i,pageSize,table,totalCon,fields);
					while (cursorless.hasNext()) {
						obj = null;
						map = null;
						obj = cursorless.next();
						if(null != obj && obj.get("_id")!=null){
							String _id=obj.get("_id")+"";
							String timestamp=obj.get("timestamp")+"";
							/**-------处理后续动作----------*/
							
							/**-------处理后续动作----------*/
						}
					}
					//获取一次分页中最后一条记录的"_id",然后作为查询条件传入到下一条执行语句中
					if (null != obj) {
						lastStr = obj.get("_id").toString();
						LoggerUtils.info(getClass(), "**************最后一条数据的唯一索引(id):"+lastStr);
					}
				}
			}
		} catch (Exception e) {
			LoggerUtils.error(getClass(), e.getMessage(),e);
		}
	}
	

 

 

 

 

  • 4
    点赞
  • 24
    收藏
    觉得还不错? 一键收藏
  • 13
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 13
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值