Bitmap方案
所谓的Bitmap就是用一个bit位来标记某个元素对应的Value,比如ID为2的用户,就用第2个bit位来表示,然后用该位的值来表示该用户是否访问过。如果要计算UV,那就只要数一下有多少个1就可以了。该方案有一个限制就是用户的ID只能为数值型的,不能基于非数值型进行统计,如UUID。Redis提供了相关命令可以实现bitmap,如下:
// 插入
setbit key offset value
//获取
getbit key offset
//计数
BITCOUNT key [start] [end]
这里offset最大值就是2^32,比如ID为2的用户可以setbit uv 2 1,来记录。最后要计算UV,就直接 BITCOUNT uv. 这步计数非常快,复杂度是O(1)。
HyperLogLog方案
Redis提供了命令实现了该方案:
redis 127.0.0.1:6379> PFADD runoobkey "redis"
1) (integer) 1
redis 127.0.0.1:6379> PFADD runoobkey "mongodb"
1) (integer) 1
redis 127.0.0.1:6379> PFADD runoobkey "mysql"
1) (integer) 1
redis 127.0.0.1:6379> PFCOUNT runoobkey
(integer) 3
参考实现:
stream.foreachRDD { rdd =>
//统计人数
rdd.foreachPartition { partition =>
//从分区所属executor的redis线程池获取一个连接.
val redis = RedisUtil.getRedis
partition.foreach { case (date, userId) =>
//统计当前userId
redis.pfadd(s"uv:$date", userId)
}
redis.close()
}
}
基于DStream计算
DStream的tranform,在该方法内部直接对RDD进行distinct操作,这样就是实现了用户UserID的去重,进而就可以计算出UV了。
private static void onlineUV(JavaPairInputDStream<String, String> lines) {
/*
* 第四步:接下来就像对于RDD编程一样基于DStream进行编程!!!原因是DStream是RDD产生的模板(或者说类),在Spark Streaming具体
* 发生计算前,其实质是把每个Batch的DStream的操作翻译成为对RDD的操作!!!
* 对初始的DStream进行Transformation级别的处理,例如map、filter等高阶函数等的编程,来进行具体的数据计算
*/
JavaPairDStream<String, String> logsDStream = lines.filter(new Function<Tuple2<String,String>, Boolean>() {
@Override
public Boolean call(Tuple2<String, String> v1) throws Exception {
String[] logs = v1._2.split("\t");
String action = logs[5];
if("View".equals(action)){
return true;
} else {
return false;
}
}