当前位置: 首页 > article >正文

Redis 的 Bitmap(位图)的使用场景

Redis 的 Bitmap(位图)是一种特殊的数据结构,它本质上是一个字符串,但可以将它看作一个由二进制位组成的数组。每个位只能是 0 或 1,因此 Bitmap 非常适合用于存储和处理大量的布尔状态信息,而且非常节省空间。

基本操作:

  • SETBIT key offset value: 设置指定偏移量(offset)上的位的值(0 或 1)。
  • GETBIT key offset: 获取指定偏移量上的位的值。
  • BITCOUNT key [start end]: 计算指定范围内值为 1 的位的数量。
  • BITOP operation destkey key [key ...]: 对多个 Bitmap 进行位运算(AND、OR、XOR、NOT),并将结果存储到新的 Bitmap 中。
  • BITPOS key bit [start] [end]: 查找指定范围内第一个值为 0 或 1 的位的位置。

应用场景:

  1. 用户签到:

    • 使用 Bitmap 存储用户的签到状态。
    • 每个用户对应一个 Bitmap,Bitmap 的每一位代表一天。
    • 例如,用户 ID 为 1000 的用户,2023 年 10 月 26 日签到,可以将 user_sign:1000 的第 299 位(从 0 开始计算,2023 年的第 299 天)设置为 1。
    // 签到
    stringRedisTemplate.opsForValue().setBit("user_sign:1000", 299, true);
    
    // 查询用户是否签到
    boolean signedIn = stringRedisTemplate.opsForValue().getBit("user_sign:1000", 299);
    
    // 统计用户当月签到次数
    long count = stringRedisTemplate.execute((RedisCallback<Long>) connection ->
            connection.bitCount("user_sign:1000".getBytes(), 273, 303)); // 计算 10 月份的签到次数
    
     // 统计用户连续签到天数 (需要遍历)
     // ...
    
    • 可以按月分key, 比如user_sign:1000:202310, 这样可以减少key的大小.
  2. 用户在线状态:

    • 使用 Bitmap 存储用户的在线状态。
    • 每个用户对应 Bitmap 的一位。
    • 用户上线时,将对应的位设置为 1;用户下线时,将对应的位设置为 0。
    • 可以配合过期时间使用,例如设置一个较短的过期时间(如 5 分钟),如果用户在过期时间内没有活动,就认为用户离线。
    // 用户上线
    stringRedisTemplate.opsForValue().setBit("online_users", 1000, true);
    stringRedisTemplate.expire("online_users", 5, TimeUnit.MINUTES);
    
    // 用户下线
    stringRedisTemplate.opsForValue().setBit("online_users", 1000, false);
    
    // 查询用户是否在线
    boolean online = stringRedisTemplate.opsForValue().getBit("online_users", 1000);
    
    // 统计在线用户数
    long count = stringRedisTemplate.execute((RedisCallback<Long>) connection -> connection.bitCount("online_users".getBytes()));
    
  3. 布隆过滤器 (Bloom Filter):

    • 布隆过滤器是一种概率型数据结构,用于判断一个元素是否可能存在于一个集合中。
    • 可以使用 Bitmap 实现布隆过滤器。
    • 将元素通过多个哈希函数映射到 Bitmap 的多个位上,并将这些位设置为 1。
    • 判断元素是否存在时,检查对应的位是否都为 1。如果任何一位为 0,则元素一定不存在;如果所有位都为 1,则元素可能存在(存在误判率)。
  4. 统计活跃用户:

    • 可以使用 Bitmap 存储每日的活跃用户。
    • 每天使用一个 Bitmap,用户 ID 作为偏移量。
    • 用户访问时,将对应的位设置为 1。
    • 统计一段时间内的活跃用户数,可以使用 BITOP 命令对多个 Bitmap 进行 OR 运算,然后使用 BITCOUNT 统计结果 Bitmap 中 1 的数量。
    // 用户访问
    stringRedisTemplate.opsForValue().setBit("active_users:2023-10-26", 1000, true);
    stringRedisTemplate.opsForValue().setBit("active_users:2023-10-27", 1000, true);
    stringRedisTemplate.opsForValue().setBit("active_users:2023-10-27", 1001, true);
    
    // 统计 2023-10-26 到 2023-10-27 的活跃用户数
    stringRedisTemplate.execute((RedisCallback<Long>) connection -> {
        connection.bitOp(RedisStringCommands.BitOperation.OR, "active_users:2023-10-26_27".getBytes(),
                "active_users:2023-10-26".getBytes(), "active_users:2023-10-27".getBytes());
        return connection.bitCount("active_users:2023-10-26_27".getBytes());
    });
    
    // 可以使用 Pipeline 批量操作,提高效率
    // ...
    
  5. 统计用户行为标签:

    • 可以使用 Bitmap 存储用户的行为标签。
    • 每个标签对应 Bitmap 的一位。
    • 例如,可以定义以下标签:
      • 0: 喜欢体育
      • 1: 喜欢音乐
      • 2: 喜欢电影
    • 用户具有哪些标签,就将对应的位设置为 1。
    • 可以方便地进行标签的交集、并集、差集运算,以实现用户群体的筛选。
    // 用户 1000 喜欢体育和音乐
    stringRedisTemplate.opsForValue().setBit("user_tags:1000", 0, true);
    stringRedisTemplate.opsForValue().setBit("user_tags:1000", 1, true);
    
    // 查询喜欢体育的用户
    // 可以使用 BITOP 和 BITCOUNT 统计喜欢体育的用户数
    // ...
    
  6. IP 黑白名单

    • 可以将IP地址转换为整数,然后使用Bitmap存储黑白名单。
    • 例如,可以将IPv4地址转换为32位整数,IPv6地址转换为128位整数。
  7. 统计独立访客(UV)

    • 可以使用一个大的 Bitmap,每一位代表一个用户。用户访问时,将对应的位设置为1.
    • BITCOUNT 可以直接统计UV.
    • 这种方式在用户量非常大时,内存占用也会很大。可以使用 HyperLogLog 作为替代方案 (有一定误差)。

优点:

  • 节省空间: Bitmap 使用位存储数据,非常节省空间。例如,存储 1 亿个用户的签到状态,只需要约 12 MB 的内存 (100000000 / 8 / 1024 / 1024)。
  • 高效的位运算: Redis 提供了高效的位运算命令(BITOP),可以快速进行交集、并集、差集等操作。
  • 快速的查找: GETBITBITPOS 命令可以快速查找指定位的值或位置。

缺点:

  • 稀疏位图可能浪费空间: 如果 Bitmap 中 1 的分布非常稀疏,可能会浪费空间。例如,如果只有少数用户签到,但用户 ID 很大,Bitmap 可能会占用大量内存,但其中大部分位都是 0。
  • 不适合存储非bool类型的数据

注意事项:

  • 合理设计键名: 键名应该具有可读性和区分度,例如 user_sign:{user_id}:{year_month}
  • 选择合适的偏移量: 偏移量应该根据业务需求进行选择,例如用户 ID、日期等。
  • 考虑数据过期: 对于不需要长期保存的数据,可以设置过期时间,以释放内存。
  • 注意内存使用: 虽然 Bitmap 很节省空间,但如果存储的数据量非常大,仍然需要注意内存使用情况。可以考虑分片存储或使用 HyperLogLog 等替代方案。
  • 注意大Key问题: 如果一个 Bitmap 存储了大量的数据, 可能会导致 Redis 阻塞. 需要根据实际情况进行拆分. 比如按月、按日拆分。

总结:

Redis Bitmap 是一种非常高效的数据结构,适用于存储和处理大量的布尔状态信息。 常见的应用场景包括用户签到、用户在线状态、布隆过滤器、统计活跃用户、用户行为标签等。 在使用 Bitmap 时,需要注意合理设计键名、选择合适的偏移量、考虑数据过期、注意内存使用,以及大key问题。


http://www.kler.cn/a/566792.html

相关文章:

  • Windows 图形显示驱动开发-WDDM 3.2-自动显示切换(五)
  • Ubuntu 下 nginx-1.24.0 源码分析 - ngx_init_cycle 函数 - 详解(2)
  • 交叉编译curl(OpenSSL)移植ARM详细步骤
  • 支付宝 IoT 设备入门宝典(下)设备经营篇
  • python 视频网站爬虫教程,爬虫入门教程(付安装包)
  • 基于大型语言模型的google浏览器翻译插件
  • Redis开启远程访问
  • IDEA-插件开发踩坑记录-第七坑-Unable to locate JNA native support library
  • 第15届 蓝桥杯 C++编程青少组中/高级选拔赛 202403 真题答案及解析
  • threeJs+vue 加载gltf模型、获取模型尺寸、播放模型动画
  • React vs Vue3深度对比与使用场景分析
  • 使用机器学习进行土地覆盖分类
  • 网络安全和爬虫的关系
  • elementUI 表格隔行换色,修改table表头背景样式
  • 排序算法归类整理对比
  • 002-快速安装 Linux 虚拟机
  • PyCharm中通过命令行执行`pip`命令下载到哪里了:虚拟环境目录下
  • 如何通过网管提升运维效率?
  • 游戏引擎学习第125天
  • Shell学习(5/6) 流程控制-函数