用mongo和redis查询排行榜统计活跃用户
Posted zhzhair-codestyle
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了用mongo和redis查询排行榜统计活跃用户相关的知识,希望对你有一定的参考价值。
统计活跃用户数(统计某个移动端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
结束语:本文的方案能解决一些高并发,大数据量的问题,但只是对于数据量不是特别巨大,又想用较低成本解决问题的一小点想法。
以上是关于用mongo和redis查询排行榜统计活跃用户的主要内容,如果未能解决你的问题,请参考以下文章