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

本文涉及的产品
Redis 开源版,标准版 2GB
推荐场景:
搭建游戏排行榜
云数据库 Tair(兼容Redis),内存型 2GB
简介: redis灵魂拷问:聊一聊bitmap使用

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日的打卡情况。下面代码是员工打卡和查询员工打卡情况:

/**
 * 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":

/**
 * 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来执行,代码如下:

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:

    redisTemplate.setKeySerializer(new StringRedisSerializer());


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


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

    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天内没有迟到的员工,代码如下:

    /**
     * 命令: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是我上面的代码所在类:

    @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);
    }

    输出如下:

      ==========50


      判断日活跃用户数量


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

        redisTemplate.opsForValue().setBit(key, number - 1, true);

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

          redisTemplate.execute((RedisCallback<Long>)  conn -> conn.bitCount(key.getBytes()));


          总结


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

            10亿/8/1024/1024=120M
            相关实践学习
            基于Redis实现在线游戏积分排行榜
            本场景将介绍如何基于Redis数据库实现在线游戏中的游戏玩家积分排行榜功能。
            云数据库 Redis 版使用教程
            云数据库Redis版是兼容Redis协议标准的、提供持久化的内存数据库服务,基于高可靠双机热备架构及可无缝扩展的集群架构,满足高读写性能场景及容量需弹性变配的业务需求。 产品详情:https://www.aliyun.com/product/kvstore &nbsp; &nbsp; ------------------------------------------------------------------------- 阿里云数据库体验:数据库上云实战 开发者云会免费提供一台带自建MySQL的源数据库&nbsp;ECS 实例和一台目标数据库&nbsp;RDS实例。跟着指引,您可以一步步实现将ECS自建数据库迁移到目标数据库RDS。 点击下方链接,领取免费ECS&amp;RDS资源,30分钟完成数据库上云实战!https://developer.aliyun.com/adc/scenario/51eefbd1894e42f6bb9acacadd3f9121?spm=a2c6h.13788135.J_3257954370.9.4ba85f24utseFl
            相关文章
            |
            8月前
            |
            存储 NoSQL Serverless
            位运算的魅力:使用Redis Bitmap高效处理百万级布尔值
            位运算的魅力:使用Redis Bitmap高效处理百万级布尔值
            367 0
            |
            8月前
            |
            存储 NoSQL BI
            Redis 实战篇:巧用 Bitmap 实现亿级海量数据统计
            Redis 实战篇:巧用 Bitmap 实现亿级海量数据统计
            197 0
            |
            2月前
            |
            存储 NoSQL PHP
            如何用Redis高效实现点赞功能?用Set?还是Bitmap?
            在众多软件应用中,点赞功能几乎成为标配。本文从实际需求出发,探讨如何利用 Redis 的 `Set` 和 `Bitmap` 数据结构设计高效点赞系统,分析其优缺点,并提供 PHP 实现示例。通过对比两种方案,帮助开发者选择最适合的存储方式。
            53 3
            |
            3月前
            |
            消息中间件 分布式计算 NoSQL
            大数据-41 Redis 类型集合(2) bitmap位操作 geohash空间计算 stream持久化消息队列 Z阶曲线 Base32编码
            大数据-41 Redis 类型集合(2) bitmap位操作 geohash空间计算 stream持久化消息队列 Z阶曲线 Base32编码
            37 2
            |
            6月前
            |
            存储 NoSQL Redis
            Redis 中bitMap使用及实现访问量
            Redis 中bitMap使用及实现访问量
            181 3
            |
            6月前
            |
            存储 NoSQL Java
            Java中使用redis的bitMap实现签到功能
            这个实现示例提供了一种灵活、高效的方式,展示了如何使用Redis来解决现实中的问题。
            350 2
            |
            6月前
            |
            存储 NoSQL 数据管理
            如何借助Redis巧妙的管理用户签到?——Bitmap篇
            Redis位操作用于高效存储分析,如用户签到。通过位操作,每个用户签到只需1位,节省空间。使用`setbit`设置签到状态,`getbit`查询,`bitcount`统计签到天数。适用于用户特征标记、系统功能开关和在线状态追踪。高效率、低空间占用,适合大数据场景。
            101 0
            |
            7月前
            |
            存储 NoSQL Redis
            蓝易云 - Redis之bitmap类型解读
            需要注意的是,虽然bitmap可以高效地存储和计算大量的位,但是它也有一些局限性,例如,它不能直接获取或设置某一范围内的所有位,也不能直接获取或设置多个不连续的位。
            33 2
            |
            8月前
            |
            NoSQL 算法 Java
            Redis入门到通关之BitMap实现签到
            Redis入门到通关之BitMap实现签到
            112 2
            |
            8月前
            |
            存储 监控 NoSQL
            使用Redis的Bitmap统计一周连续登录的用户
            使用Redis的Bitmap统计一周连续登录的用户
            249 1