java使用redis进行位图法统计活跃用户

位图法
位图是通过将数组下标与应用中的一些值关联映射,数组中该下标所指定的位置上的元素可以用来标识应用中值的情况(是否存在或者数目 或者计数等),位图数组中每个元素在内存中占用1位,所以可以节省存储空间。位图是一种非常简洁快速的数据结构,它能同时使存储空间和速度最优化。如可用一个10位长的字符串来表示一个所有元素都小于10的简单的非负整数集合,例如,可以用如下字符串表示集合{1,2,4,5,8} ,对应位置数字存在标记为1,否则标记为0。

JAVA
Bitmap(即Bitset)
Bitmap是一串连续的2进制数字(0或1),每一位所在的位置为偏移(offset),在bitmap上可执行AND,OR,XOR以及其它位操作。
BitSet是java的一个类,详细的使用可以参考https://www.cnblogs.com/xupengzhang/p/7966755.html。其常用的方法有get(),set(),cardinality(),and(),or(),xor()等等

位图计数(Population Count)
位图计数统计的是bitmap中值为1的位的个数。位图计数的效率很高,例如,一个bitmap包含10亿个位,90%的位都置为1,在一台MacBook Pro上对其做位图计数需要21.1ms。SSE4甚至有对整形(integer)做位图计数的硬件指令。

Redis
Redis允许使用二进制数据的Key(binary keys) 和二进制数据的Value(binary values)。在redis中,字符串是以二进制的形式存储的,因此位图在redis中并不是一种数据类型,而是一种字符串的表现形式。
在redis中如果使用位图,其常见的命令:getbit,setbit,bitop,BITCOUNT等等。
关于getbit,setbit,bitop的命令可以参考https://blog.csdn.net/a692055612/article/details/79717146

日活跃用户
为了统计今日登录的用户数,我们建立了一个bitmap,每一位标识一个用户ID。当某个用户访问我们的网页或执行了某个操作,就在bitmap中把标识此用户的位置为1。在Redis中获取此bitmap的key值是通过用户执行操作的类型和时间戳获得的。

这个简单的例子中,每次用户登录时会执行一次redis.setbit(daily_active_users, user_id, 1)。将bitmap中对应位置的位置为1,时间复杂度是O(1)。统计bitmap结果显示有今天有9个用户登录。Bitmap的key是daily_active_users,它的值是1011110100100101。

因为日活跃用户每天都变化,所以需要每天创建一个新的bitmap。我们简单地把日期添加到key后面,实现了这个功能。例如,要统计某一天有多少个用户至少听了一个音乐app中的一首歌曲,可以把这个bitmap的redis key设计为play:yyyy-mm-dd-hh。当用户听了一首歌曲,我们只是简单地在bitmap中把标识这个用户的位置为1,时间复杂度是O(1)。

JAVA代码示例

public void setActiveUserCount(Integer id) {
    Jedis jedis = factory.getConnection().getNativeConnection();
    Date currentTime = new Date();
    String currentStr = new SimpleDateFormat("yyyy-MM-dd").format(currentTime);
    String key = activeName+currentStr;
    jedis.setbit(key, id, true);
}

解析:

以id为偏移量,1为用户今天已登录,0为用户今天未登陆。

示例代码是spring配置管理的factory是spring管理的JedisConnectionFactory。关于如何在spring中配置redis,并且如何使用Jedis可百度

统计活跃用户
需求:

统计七天内有进行至少一次登录操作的用户人数

思考:

采取按位存储的方式,bit的下标为用户的id,如果用户id的下标value为1,那么该用户在当天有进行登录
将所用的用户生成一个位图(0/1 未登录/登录)
统计七天内,有过一次登录的用户,那么他就是活跃用户,所以是获取七天的字节数组,取他们的并集,用or。如果想获取七天都有登录的用户,那么需要的是取他们的交集,用and
@Autowired
JedisConnectionFactory factory;
private static final String activeName = “activeUser:”;

public int getActiveUserCount() {
Jedis jedis = factory.getConnection().getNativeConnection();
Date currentTime = new Date();
BitSet all = new BitSet();
for(int i=0;i<7;i++){
//获得前一天的日期
currentTime=DateUtils.addDays(currentTime, -1);
String currentStr = new SimpleDateFormat(“yyyy-MM-dd”).format(currentTime);
String key = activeName+currentStr;
//获得这一天日期的活跃用户字节数组
byte[] loginByte = jedis.get(key.getBytes());
//有可能当天没有一个用户登录
if(loginByte!=null){
BitSet user = BitSet.valueOf(loginByte);
//取其并集
all.or(user);
}
}
//统计人数
int count = all.cardinality();
return count;
}
解析:

如果需求是统计七天内都有登录的用户人数,那么取其交集,用and方法

总结:
上面代码示例并没有将数据截图展示,但个人私下已经进行了数据填充,且进行了测试,代码通过。除了使用java的BitSet的方法进行统计,还能使用redis的命令进行统计,java只需获取命令返回接口即可。

版权声明:本文为CSDN博主「没有不忧伤的故事」的原创文章,遵循 CC 4.0 BY-SA 版权协议,转载请附上原文出处链接及本声明。
原文链接:https://blog.csdn.net/qq_32020035/article/details/81197284

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值