最近项目在做网站用户数据新访客统计,数据存储在MongoDB中,统计的数据其实也并不是很大,1000W上下,但是公司只配给我4G内存的电脑,让我程序跑起来气喘吁吁...很是疲惫不堪。

    最常见的问题莫过于查询MongoDB内存溢出,没办法只能分页查询。这种思想大家可能都会想到,但是如何分页,确实多有门道!

    网上用的最多的,也是最常见的分页采用的是skip+limit这种组合方式,这种方式对付小数据倒也可以,但是对付上几百上千万的大数据,却只能望而兴叹...

    经过网上各种查找资料,寻师问道的,发现了一种速度足以把skip+limit组合分页甩出几条街的方法。

    思路: 条件查询+排序+限制返回记录。边查询,边排序,排序之后,抽取第一次分页中的最后一条记录,作为第二次分页的条件,进行条件查询,以此类推....

    先上代码: 

 /**
     * 小于指定日期的所有根据UUID分组的访问记录
     * @param 指定日期
     * @return 所有访问记录的MAP
     */
    public static Multimap<String, Map<String, String>> getOldVisitors(String date){
        
        //每次查询的记录数
        int pagesize = 100000;
        
        //mongodb中的"_id"
        String objectId = "";
        
        //方法的返回值类型,此处用的google guava
        Multimap<String, Map<String, String>> mapless = null;
        
        //查询的条件
        BasicDBObject queryless = new BasicDBObject(),fields = new BasicDBObject(),field = new BasicDBObject();
        
        //初始化返回的mongodb集合操作对象,大家可以写个数据连接池
        dbCol = init();
        
        //查询指定字段,字段越少,查询越快,当然都是一些不必要字段
        field.put("uuid",1);
        
        fields.put("uuid", 1);
        
        fields.put("initTime", 1);
        
        //小于指定日期的条件
        String conditionless = TimeCond.getTimeCondless(date);
        
        queryless.put("$where", conditionless);
        
        DBCursor cursorless = dbCol.find(queryless,field);
        
        //MongoDB在小于指定日期条件下,集合总大小
        int countless = cursorless.count();
        
        //查询遍历的次数 circleCountless+1
        int circleCountless = countless/pagesize;
        
        //取模,这是最后一次循环遍历的次数
        int modless = countless%pagesize;
        
        //开始遍历查询
        for (int i = 1; i <=circleCountless+1; i++) {
            
            //文档对象
            DBObject obj = null;
            
            //将游标中返回的结果记录到list集合中,为什么放到list集合中?这是为后面guava 分组做准备
            List<Map<String, String>> listOfMaps = new ArrayList();
            
            //如果条件不为空,则加上此条件,构成多条件查询,这一步是分页的关键
            if (!"".equals(objectId)) {
                
                  //我们通过文档对象obj.get("_id")返回的是不带ObjectId(),所以要求此步骤
                               ObjectId id = new ObjectId(objectId);
                
                   queryless.append("_id", new BasicDBObject("$gt",id));
                
            }
            
            if (i<circleCountless+1) {
                
            cursorless = dbCol.find(queryless,fields).sort(new BasicDBObject("_id", 1)).limit(pagesize);
                
            }else if(i==circleCountless+1){//最后一次循环
                
                cursorless = dbCol.find(queryless,fields).limit(modless);
            }
            
                    //将游标中返回的结果记录到list集合中,为什么放到list集合中?这是为后面guava 分组做准备
                while (cursorless.hasNext()) {
                    
                    obj = cursorless.next();
                    
                    listOfMaps.add((Map<String, String>) obj);
                    
                }
                //获取一次分页中最后一条记录的"_id",然后作为条件传入到下一个循环中
                if (null!=obj) {
                    
                     objectId = obj.get("_id").toString();
                     
                    }
            //第一次分组,根据uuid分组,分组除今天之外的历史数据
        mapless = Multimaps.index(
                      listOfMaps,new Function<Map<String, String>, String>() {
                          public String apply(final Map<String, String> from) {
                             
                                  return from.get("uuid");    
                      }
                 });
              
          }    
    
        return mapless;
    }
View Code

相关文章:

  • 2022-12-23
  • 2021-12-31
  • 2022-12-23
  • 2022-12-23
  • 2022-01-23
  • 2021-12-04
  • 2022-02-16
  • 2021-11-27
猜你喜欢
  • 2021-09-25
  • 2022-12-23
  • 2022-12-23
  • 2022-12-23
  • 2022-01-03
  • 2021-07-20
  • 2021-06-01
相关资源
相似解决方案