spring boot集成mongo统计活跃用户数

  

  统计活跃用户数(统计某个移动端app软件在各个下载渠道的活跃设备数,以起始时间,版本号,系统类型等作为查询条件,这里为了简便起见,不考虑查询条件)。技术架构:java8,spring boot2.0.0,mysql,mongodb,mybatis,swagger,idea,maven。
  添加测试数据:新建4个表(建4个表是为了用多线程添加数据比较快,要不然“我没得耐心等”),包括APP_CHANNEL--下载渠道,DEVICE_ID--设备id号,DEVICE_HASHCODE--设备id号的hash值,DEVICE_HASHCODE_IDX--hash值的绝对值除以16384的余数。将1000w条记录插入这4个表,每个表250万,然后新建32个表,根据DEVICE_HASHCODE_IDX对32取模,将四个表的数据按类别插入到这32个表中,移动设备被分成了32个类,此时再也不用担心select app_channel,count(distinct device_id) from t group by app_channel;的效率了,如果你用的是专业的服务器,还有多台机器,你完全可以放到更多甚至几百个表中,这样就更无敌了。好了,现在我不关心去重再计数(MySQL的大表去重计数慢到你怀疑人生)的问题了,我只需要将每个表的数据合到一起(总数据量<=分表的个数*下载渠道个数),再分组求和(select app_channel,sum(device_count) from t group by app_channel)。部分代码如下:
@Override//按设备分类将1000w数据放到32个表中
public void insertTables(int tableCount) {
  IntStream.range(0,tableCount).parallel().forEach(i->this.insertOneTable(i,tableCount));
}
private void insertOneTable(int i,int tableCount){
  commonMapper.truncateTable(tableName + "_" + i);
  for (int k = 0; k < 4; k++) {
    List<StartRecordMapperRequest> list0 = new ArrayList<>(1000_0000/tableCount/4);
    for (int j = i; j < 16384; j+=tableCount) {
      List<StartRecordMapperRequest> list = commonMapper.getStartDataByRem(tableName + k, j);
      list0.addAll(list);
    }
    int size = list0.size();
    for (int j = 0; j < size/10000 + 1; j++) {
      List<StartRecordMapperRequest> list = list0.subList(j*10000,Math.min(j*10000 + 10000,size));
      commonMapper.insertTables(list,tableName + "_" + i);
    }
  }
  System.out.println(i + " =================");
}
  查询活跃用户数:将32个表的活跃设备数据先查出来,即select app_channel,count(distinct device_id) from t group by app_channel;插入到mongo文档,再从mongo分组求和即可得到最终的活跃设备数,部分代码如下:
@Override
public List<Document> getActiveCount(int tableCount) {
  mongoTemplate.dropCollection(ActiveChannelCountMongo.class);
  if(!mongoTemplate.collectionExists(ActiveChannelCountMongo.class))
  IntStream.range(0,tableCount).parallel().forEach(this::getActiveCountOne);
  TypedAggregation<ActiveChannelCountMongo> aggregation = Aggregation.newAggregation(
    ActiveChannelCountMongo.class,
    project("appChannel", "activeCount"),//查询用到的字段
    // match(Criteria.where("dateTime").lte(Date.valueOf(todayZero).getTime()).gte(Date.valueOf(yesterday).getTime())),
    group("appChannel").sum("activeCount").as("activeCount"),
    sort(Sort.Direction.DESC,"activeCount"),
    project("appChannel", "activeCount").and("appChannel").previousOperation()//输出字段,后面是取别名
  ).withOptions(newAggregationOptions().allowDiskUse(true).build());//内存不足就到磁盘读写
  AggregationResults<Document> results = mongoTemplate.aggregate(aggregation, ActiveChannelCountMongo.class, Document.class);
  return results.getMappedResults();
}

private void getActiveCountOne(int i){
  List<ActiveChannelCount> list = viewMapper.getActiveCount(tableName + i);
  mongoTemplate.insert(list,ActiveChannelCountMongo.class);
}

  调接口看执行时间和返回结果:访问接口文档--http://localhost/swagger-ui.html/,调接口输出如下日志:
前端调用方法开始----getActiveCount---->:#{"URL地址":/view/getActiveCount, "HTTP方法":GET,参数:, "tableCount":32}
前端调用方法结束----getActiveCount---->:返回值: BaseResponse{code=0, msg='获取数据成功', data=[Document{{activeCount=111792, appChannel=appStore}}, Document{{activeCount=73757, appChannel=yingyongbao}}, Document{{activeCount=55640, appChannel=baiduyingyong}}, Document{{activeCount=55605, appChannel=vivo}}, Document{{activeCount=36997, appChannel=xiaomi}}, Document{{activeCount=36991, appChannel=360yingyong}}, Document{{activeCount=18575, appChannel=samsung}}, Document{{activeCount=18528, appChannel=iTools}}, Document{{activeCount=18483, appChannel=oppo}}, Document{{activeCount=18472, appChannel=htc}}, Document{{activeCount=18457, appChannel=huawei}}, Document{{activeCount=18374, appChannel=wandoujia}}, Document{{activeCount=18329, appChannel=mezu}}]}
2018-11-11 09:45:26,595 INFO - [http-nio-80-exec-13 ] c.e.f.c.m.i.RequestTimeConsumingInterceptor : /view/getActiveCount 3010ms

  结束语:本文的方案能解决一些高并发,大数据量的问题,但只是对于数据量不是特别巨大,又想用较低成本解决问题的一小点想法。

原文地址:https://www.cnblogs.com/zhzhair-coding/p/9941542.html