《Redis深度历险》学习笔记:位图

  • 对于开关 / 签到类型的业务数据,可以考虑采用 bitmap(即位图)的数据结构进行存储

  • 位图的最小单元是 bit,每个 bit 的取值只能是 0 或 1,如下图所示:
    位图

  • Redis 字符位顺序与其 ASCII 码二进制值位顺序相反,多个字符会自动扩容连接在一起:
    image.png image.png

  • set 整存,get 整取,setbit 零存,getbit 零取:

    127.0.0.1:6379> set w he
    127.0.0.1:6379> get w
    "he"
    
    127.0.0.1:6379> setbit s 1 1
    127.0.0.1:6379> setbit s 2 1
    127.0.0.1:6379> setbit s 4 1
    127.0.0.1:6379> setbit s 9 1
    127.0.0.1:6379> setbit s 10 1
    127.0.0.1:6379> setbit s 13 1
    127.0.0.1:6379> setbit s 15 1
    127.0.0.1:6379> getbit w 4
    (integer) 1
    127.0.0.1:6379> get s
    "he"
    
  • bitcount 命令统计指定位置范围内 1 的个数。
    eg. 统计签到天数。起始参数 [start, end] 为索引值,必须为 8 的倍数。

  • bitpos 命令查找指定范围内出现的第一个 0 或 1。
    eg. 查找签到第一天。起始参数 [start, end] 为索引值,必须为 8 的倍数。

  • bitfield 可以一次操作多个位,子指令 get/set/incrby 都可以对指定位片段进行读写,参数 u 为无符号,参数 i 为有符号。

  • bitfield 处理上限为 64 个连续的位,否则需要执行子指令:

    127.0.0.1:6379> bitfield w get u4 0 get u3 2 get i4 0 get i3 2
    1) (integer) 6     #index=0,无符号取 4 位
    2) (integer) 5     #index=2,无符号取 3 位
    3) (integer) 6     #index=0,有符号取 4 位
    4) (integer) -3    #index=2,有符号取 3 位,注意负数的补码计算
    
  • Redis 的 integer 是有符号数,最大 64 位,不能传递 64 位无符号值,所以无符号值最大值为 63 位

  • 自增操作需要注意位数据溢出的问题:

    • 无符号数自增溢出归零(eg. u127+1=0
    • 有符号数自增溢出变为最小负数(eg.i127+1=-128
  • bitfield 提供另外两种溢出策略:

    • 饱和截断 SAT:停留在最大/最小值。
    • 失败不执行 FAIL:报错不执行。
updatedupdated2023-09-272023-09-27