• 06- Redis 中的 BitMap 数据类型和应用场景


    1. 介绍

    Bitmap,即位图,是一串连续的二进制数组(0 和 1),可以通过偏移量(offset)定位元素。BitMap 通过最小的单位 bit 来进行 0 | 1 的设置,表示某个元素的值或者状态,时间复杂度为O(1)。

    由于 bit 是计算机中最小的单位,使用它进行储存将非常节省空间,特别适合一些数据量大且使用二值统计的场景

    2. 内部实现

    BitMap 本身是用 String 类型作为底层数据结构实现的一种统计二值状态的数据类型。

    String 类型是会保存为二进制的字节数组,所以,Redis 就把字节数组的每个 bit 位利用起来,用来表示一个元素的二值状态,你可以把 BitMap 看作是一个 bit 数组。

    3. 常用命令

    bitmap 基本操作:

    1. # 设置值,其中 value 只能是 01
    2. SETBIT key offset value
    3. # 获取值
    4. GETBIT key offset
    5. # 获取指定范围内值为 1 的个数
    6. # startend 以字节为单位
    7. BITCOUNT key start key

    bitmap 运算操作:

    1. # BitMap 间的运算
    2. # operatinos 位移操作符,枚举值
    3. AND 与运算 &
    4. OR 或运算 |
    5. XOR 异或 ^
    6. NOT 取反 ~
    7. # result 计算的结果,会存储在该 key
    8. # key1 ... keyn 参与运算的 key,可以有多个,空格分割,not 运算只能一个 key
    9. # 当 BITOP 处理不同长度的字符串时,较短的那个字符串所缺少的部分会被看作 0。返回值是保存到 destkey 的字符串的长度(以字节 byte 为单位),和输入 key 中最长的字符串长度相等。
    10. BITOP [operations] [result] [key1] [keyn...]
    11. # 返回指定 key 中第一次出现指定 value(0/1)的位置
    12. BITPOS [key] [value]

    4. 应用场景

    BitMap 类型非常适合二值状态统计的场景,这里的二值状态就是指集合元素的取值只有 0 和 1 两种,在记录海量数据时,BitMap 能够有效地节省内存空间。

    4.1 签到统计

    在签到打卡的场景中,我们只用记录签到(1)或未签到(0),所以它就是非常典型的二值状态。

    签到统计时,每个用户一天的签到用 1 个 bit 位就能表示,一个月(假设是 31 天)的签到情况用 31 个 bit 位就可以,而一年的签到也只需要用 365 个 bit 位,根本不用太复杂的集合类型。

    假设我们要统计 ID 100 的用户在 2022 年 6 月份的签到情况,就可以按照下面的步骤进行操作。

    第一步,执行下面的命令,记录该用户 6 月 3 号已签到。

    SETBIT uid:sign:100:202206 2 1

    第二步,检查该用户 6 月 3 日是否签到。

    GETBIT uid:sign:100:202206 2

    第三步,统计该用户在 6 月份的签到次数。

    BITCOUNT uid:sign:100:202206

    这样,我们就知道该用户在 6 月份的签到情况了。

    如何统计这个月首次打卡时间呢?

    Redis 提供了 BITPOS key bitValue [start][end] 指令,返回数据表示 BitMap 中第一个值为 bitValue 的 offset 位置。

    在默认情况下,命令将检测整个位图,用户可以通过可选的 start 参数和 end 参数指定要检测的范围,所以我们可以通过执行这条命令来获取 userId = 100 在

    2022 年 6 月份首次打卡日期:

    BITPOS uid:sign:100:202206 1

    需要注意的是,因为 offset 从 0 开始的,所以我们需要将返回的 value + 1。

    4.2 判断用户登录态

    BitMap 提供了 GETBIT、SETBIT 操作,通过一个偏移值 offset 对 bit 数组的 offset 位置的 bit 位进行读写操作,需要注意的是 offset 从 0 开始。

    只需要一个 key = login_status 表示存储用户登录状态集合数据,将用户 ID 作为 offset,在线就设置为 1,下线设置为 0。通过 GETBIT 判断对应的用户是否在线。5000 万用户只需要 6 MB 的空间。

    假如我们要判断 ID = 10086 的用户的登录情况:

    第一步,执行以下指令,表示用户已登录。

    SETBIT login_status 10086 1

    第二步,检查该用户是否登录,返回值 1 表示已登录。

    GETBIT login_status 10086

    第三步,登出,将 offset 对应的 value 设置为 0。

    SETBIT login_status 10086 0

    4.3 连续签到用户总数

    如何统计出这连续 7 天连续打卡用户总数呢?

    我们把每天的日期作为 BitMap 的 key,userId 作为 offset,若是打卡则将 offset 位置的 bit 设置成 1。

    key 对应的集合的每个 bit 位的数据则是一个用户在该日期的打卡记录。

    一共有 7 个这样的 BitMap,如果我们能对这 7 个 BitMap 的对应的 bit 位做 【与】运算,同样的 userId offset 都是一样的,当一个 userId 在 7 个 BitMap 对应的 offset 位置的 bit = 1就说明该用户 7 天连续打卡。

    结果保存到一个新的 BitMap 中,我们再通过 BITCOUNT 统计 bit = 1的个数便得到了连续 7 打卡的用户总数了。

    Redis 提供了 BITOP operation destkey key [key ...] 这个指令用于对一个或者多个 key 的 BitMap 进行位元操作。

    • operation 可以是 andORNOTXOR。当 BITOP 处理不同长度的字符串时,较短的那个字符串所缺少的部分会被看作 0。空的 key 也被看作是包含 0 的字符串序列。

    假设要统计 3 天连续打卡的用户数,则是将三个 BitMap 进行 AND 操作,并将结果保存到 destmap 中,接着对 destmap 执行 BITCOUNT 统计,如下命令:

    1. # 与操作
    2. BITOP AND destmap bitmap:01 bitmap:02 bitmap:03
    3. # 统计 bit= 1 的个数
    4. BITCOUNT destmap

    即使一天产生一个亿的数据,BitMap 占用的内存也不大, 大约占 12 MB 的内存(10^8/8/1024/1024),7 天的 Bitmap 的内存开销约为 84 MB。同时我们最好给 Bitmap 设置过期时间,让 Redis 删除过期的打卡机数据,节省内存。

  • 相关阅读:
    制作手机IOS苹果ipa应用的重签名工具
    Redis Enable keyspace notifications
    后端工程化 | SpringBoot 知识点
    oracle11g表+数据完美迁移到10g解决方案
    学习java的第三十九天,Callable多线程接口、静态代理、lambda表达式、线程状态、线程停止、线程休眠、线程强制执行的基础认知
    python编写修改sqlmap进行_WAF绕过
    openssl漏洞检查修复
    RabbitMQ-网页使用消息队列
    随机森林项目实战---气温预测
    【LeetCode】 412. Fizz Buzz
  • 原文地址:https://blog.csdn.net/YoungSoulwt/article/details/139294338