zoukankan      html  css  js  c++  java
  • 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

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

  • 相关阅读:
    MySql数据类型
    mysql中char,varchar,text区别
    php错误提示:date_default_timezone_get
    才储分析
    js 阻止后续事件
    大型高性能网站的十项规则
    为rand函数加入随机数种子
    php-通过共享内存实现消息队列和进程通信
    PHP比较有用的常量
    json处理内容中多双引号的情况
  • 原文地址:https://www.cnblogs.com/zhzhair-coding/p/9941542.html
Copyright © 2011-2022 走看看