前往小程序,Get更优阅读体验!
立即前往
首页
学习
活动
专区
工具
TVP
发布
社区首页 >专栏 >redis灵魂拷问:聊一聊bitmap使用

redis灵魂拷问:聊一聊bitmap使用

作者头像
jinjunzhu
发布2020-12-02 10:49:57
3920
发布2020-12-02 10:49:57
举报
文章被收录于专栏:个人开发个人开发

bitmap是redis的一种扩展数据类型,主要用于二值状态统计,比如公司记录员工打卡记录,电商网站记录用户登录行为,积分商城记录用户签到情况。

bigmap底层使用的是String的数据结构,而String保存在计算机中的格式是二进制的字节数组,这样bitmap就充分利用了每个字节的bit位,大大节省了内存开销。

下面我们看一下bitmap的使用。

员工打卡

假如一个公司有100个员工,公司要对员工11月份的打卡行为进行统计,我们可以为11月份每一天分配一个bitmap,这个bitmap保存100个bit位,来记录员工的打卡行为。

注意:bitmap偏移量从0开始,所以100个bit位是从0~99,依次记录1-100号员工。

我们定义bitmap的key格式为:signed:20201101,记录2020年11月1日的打卡情况。下面代码是员工打卡和查询员工打卡情况:

代码语言:javascript
复制
/**
 * SETBIT命令
 * 员工打卡
 * 时间复杂度:O(1)
 */
public void sign(String key, int employeeNumber){
    redisTemplate.opsForValue().setBit(key, employeeNumber - 1, true);
}

/**
 * GETBIT命令
 * 查看员工打卡情况
 * 时间复杂度:O(1)
 */
public boolean isSigned(String key,int employeeNumber){
    return redisTemplate.opsForValue().getBit(key, employeeNumber - 1);
}

我们可以查看某一天的打卡总人数,代码如下,入参:"signed:20201101":

代码语言:javascript
复制
/**
 * BITCOUNT命令
 * 查看某一天的打卡人数
 * 时间复杂度:O(N)
 */
public Long signedCount(String key){
    return (Long) redisTemplate.execute((RedisCallback<Long>)  conn -> conn.bitCount(key.getBytes()));
}

这样我们就能根据打卡人数来判断当天的迟到人数比例。

注意:上面的sign方法必须设置key的序列化采用StringRedisSerializer,否则查询打卡情况是查不到的。如果不设置StringRedisSerializer,上面的sign和isSigned改为使用conn来执行,代码如下:

代码语言:javascript
复制
public void sign(String key, int employeeNumber){
    redisTemplate.execute((RedisCallback<Boolean>)  conn -> conn.setBit(key.getBytes(), employeeNumber - 1, true));
}
public boolean isSigned(String key, int employeeNumber){
    return redisTemplate.execute((RedisCallback<Boolean>)  conn -> conn.getBit(key.getBytes(), employeeNumber - 1));
}

或者使用下面代码来设置RedisTemplate的setKeySerializer:

代码语言:javascript
复制
redisTemplate.setKeySerializer(new StringRedisSerializer());

那如果想看当月没有迟到过的员工呢?这个时候就要用到交集了,对当月每天的bitmap做交集,值为1的员工就是没有迟到过的。

这时就要用到bitmap的聚合运算了,命令BITOP, 支持AND(与)、OR(或), XOR(异或) and NOT(非)运算,除了NOT后面跟一个bitmap外,其他3种聚合运算后面都可以跟多个bitmap,命令如下:

代码语言:javascript
复制
BITOP AND destkey srckey1 srckey2 srckey3 ... srckeyN
BITOP OR destkey srckey1 srckey2 srckey3 ... srckeyN
BITOP XOR destkey srckey1 srckey2 srckey3 ... srckeyN
BITOP NOT destkey srckey

为了让demo简单一些,我这里给出一个查看2天内没有迟到的员工,代码如下:

代码语言:javascript
复制
/**
 * 命令:BITOP
 * 复杂度:O(N)
 * 整个月全勤的员工数量,这里用2天代表整个月
 * @param key1 第一天
 * @param key2 第二天
 */
public Long signedAllMonth(String key1, String key2){
    String andMap = "signedAllMonth11";
    redisTemplate.execute((RedisCallback)  conn -> conn.bitOp(RedisStringCommands.BitOperation.AND, andMap.getBytes(), key1.getBytes(), key2.getBytes()));
    return (Long) redisTemplate.execute((RedisCallback<Long>)  conn -> conn.bitCount(andMap.getBytes()));
}

下面我再给出一段测试代码,这段代码模拟有50个员工全勤,bitMapService是我上面的代码所在类:

代码语言:javascript
复制
@Test
public void testSignedAllMonth(){
    for (int i = 1; i <= 100; i++){
        bitMapService.sign("signed:20201101", i);
    }
    for (int i = 1; i <= 100; i += 2){
        bitMapService.sign("signed:20201102", i);
    }
    Long count = bitMapService.signedAllMonth("signed:20201101", "signed:20201102");
    System.out.println("=========="+count);
}

输出如下:

代码语言:javascript
复制
==========50

判断日活跃用户数量

比如网站有10万个用户,我们要判断当天的日活用户。这样我们创建一个长度为10万的bitmap,每个用户id占一个位,我们定义key为:user:login,number为用户编号。当有用户登录时,调用下面的方法:

代码语言:javascript
复制
redisTemplate.opsForValue().setBit(key, number - 1, true);

日终的时候,我们用下面的方法就可以判断出日活用户:

代码语言:javascript
复制
redisTemplate.execute((RedisCallback<Long>)  conn -> conn.bitCount(key.getBytes()));

总结

bitmap广泛地运用在二值计算的场景,对于一个二值状态只用一个bit位就可以,非常节约内存。比如我们对一个10亿的用户进行日活计算,占用的空间只有120M:

代码语言:javascript
复制
10亿/8/1024/1024=120M

官网链接:

代码语言:javascript
复制
https://redis.io/commands/bitop
本文参与 腾讯云自媒体分享计划,分享自微信公众号。
原始发表:2020-11-26,如有侵权请联系 cloudcommunity@tencent.com 删除

本文分享自 jinjunzhu 微信公众号,前往查看

如有侵权,请联系 cloudcommunity@tencent.com 删除。

本文参与 腾讯云自媒体分享计划  ,欢迎热爱写作的你一起参与!

评论
登录后参与评论
0 条评论
热度
最新
推荐阅读
相关产品与服务
云数据库 Redis
腾讯云数据库 Redis(TencentDB for Redis)是腾讯云打造的兼容 Redis 协议的缓存和存储服务。丰富的数据结构能帮助您完成不同类型的业务场景开发。支持主从热备,提供自动容灾切换、数据备份、故障迁移、实例监控、在线扩容、数据回档等全套的数据库服务。
领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档