当前位置: 代码网 > it编程>数据库>Redis > Redis 中的 BitMaps(位图)命令详解

Redis 中的 BitMaps(位图)命令详解

2025年09月22日 Redis 我要评论
redis提供的bitmaps这个“数据结构”可以实现对位的操作。它本身不是一种数据结构,实际上就是string(字符串)数据类型,但是它可以对字符串的位进行操作。可以把 bi

redis提供的bitmaps这个“数据结构”可以实现对位的操作。它本身不是一种数据结构,实际上就是string(字符串)数据类型,但是它可以对字符串的位进行操作。可以把 bitmaps想象成一个以位为单位的数组,数组中的每个单元只能存0或者1,数组的下标在bitmaps中叫做偏移量。单个 bitmaps 的最大长度是512mb,即2^32个比特位。

现代计算机用二进制位作为信息的基础单位,1个字节等位8位,例如 big 字符串是由3个字节组成,但实际在计算机存储时将其用二进制表示,big 分别对应的ascii码分别是98、105、103,对应的二进制分别是01100010、01101001和01100111,如下图:

bitmaps本身不是一种数据类型, 实际上它就是字符串,但是它可以对字符串的位进行操作。可以把 bitmaps 想象成一个以位为单位的数组,数组的每个单元只能存储0和1,数组的下标在 bitmaps 中叫做偏移量。

合理地使用位能够有效地提高内存使用率和开发效率,很适合用于签到这类场景。比如按月进行存储,一个月最多31天,那么我们将该月用户的签到缓存二进制就是 00000000000000000000000000000000,当某天签到将0改成1即可,而且 redis 提供 对bitmap 的很多操作比如存储、获取、统计等指令,使用起来非常方便。

getbit key offset (对 key 所储存的字符串值,获取指定偏移量上的位(bit)。)

获取位图指定索引的值:

127.0.0.1:6379> flushall
ok
127.0.0.1:6379> set hello big
ok
127.0.0.1:6379> getbit hello 0
(integer) 0
127.0.0.1:6379> getbit hello 15
(integer) 1
127.0.0.1:6379> getbit hello 10
(integer) 1

setbit key offset value(对 key 所储存的字符串值,设置或清除指定偏移量上的位(bit)。)

给位图指定索引设置值,返回该索引位置的原始值:

127.0.0.1:6379> flushall
ok
127.0.0.1:6379> set hello big
ok
127.0.0.1:6379> getbit hello 7
(integer) 0
127.0.0.1:6379> setbit hello 7 1
(integer) 0
127.0.0.1:6379> getbit hello 7
(integer) 1
127.0.0.1:6379> get hello
"cig"

bitcount key [start end](计算给定字符串中,被设置为 1 的比特位的数量)

获取位图指定范围(start到end,单位为字节,如果不指定就是获取全部)位值为1的个数:

默认情况下整个字符串都会被进行计数,通过指定额外的 start 或 end 参数,可以让计数只在特定的位上进行。start、end 是指 bit 组的字节的下标数,二者皆包含。

127.0.0.1:6379> flushall
ok
127.0.0.1:6379> set hello big
ok
127.0.0.1:6379> bitcount hello
(integer) 12
127.0.0.1:6379> getbit hello 7
(integer) 0
127.0.0.1:6379> setbit hello 7 1
(integer) 0
127.0.0.1:6379> bitcount hello 
(integer) 13
127.0.0.1:6379> bitcount hello 0 1
(integer) 8
127.0.0.1:6379> bitcount hello 0 2
(integer) 13
127.0.0.1:6379> bitcount hello 1 1
(integer) 4
127.0.0.1:6379> bitcount hello 1 2
(integer) 9
127.0.0.1:6379> bitcount hello 2 2
(integer) 5

bitmap 对于一些特定类型的计算非常有效。假设现在我们希望记录自己网站上的用户的上线频率,比如说,计算用户 a 上线了多少天,用户 b 上线了多少天,诸如此类,以此作为数据,从而决定让哪些用户参加 beta 测试等活动——这个模式可以使用setbit和bitcount来实现。
比如说,每当用户在某一天上线的时候,我们就使用 setbit ,以用户名作为 key ,将那天所代表的网站的上线日作为 offset 参数,并将这个 offset 上的为设置为 1 。举个例子,如果今天是网站上线的第 100 天,而用户 peter 在今天阅览过网站,那么执行命令setbit peter 100 1;如果明天 peter 也继续阅览网站,那么执行命令setbit peter 101 1,以此类推。
当要计算 peter 总共以来的上线次数时,就使用 bitcount 命令:执行bitcount peter,得出的结果就是 peter 上线的总天数。
前面的上线次数统计例子,即使运行 10 年,占用的空间也只是每个用户 10*365 比特位(bit),也即是每个用户 456 字节。对于这种大小的数据来说, bitcount 的处理速度就像 get 和 incr 这种 o(1)复杂度的操作一样快。
如果你的 bitmap 数据非常大,那么可以考虑使用以下两种方法:

  1. 将一个大的 bitmap 分散到不同的 key 中,作为小的 bitmap 来处理。使用 lua 脚本可以很方便地完成这一工作。
  2. 使用 bitcount 的 start 和 end 参数,每次只对所需的部分位进行计算,将位的累积工作(accumulating)放到客户端进行,并且对结果进行缓存(caching)。

还有对于一些签到统计场景也一样非常有效,占用空间又小。

bitop and|or|not|xor destkey key [key…] (对一个或多个保存二进制位的字符串 key 进行位元操作,并将结果保存到 destkey 上。)

做多个bitmap的and(交集)、or(并集)、not(非)、xor(异或)操作并将结果保存到 destkey 中:
语法:bitop operation destkey key[key ...]
operation 可以是 and 、 or 、 not 、 xor 这四种操作中的任意一种:

  • bitop and destkey key[key …]:对一个或多个 key 求逻辑并,并将结果保存到 destkey 。
  • bitop or destkey key[key …]:对一个或多个 key 求逻辑或,并将结果保存到 destkey 。
  • bitop xor destkey key[key …]:对一个或多个 key 求逻辑异或,并将结果保存到 destkey 。
  • bitop not destkey key :对给定 key 求逻辑非,并将结果保存到 destkey 。

除了 not 操作之外,其他操作都可以接受一个或多个 key 作为输入。

127.0.0.1:6379> flushall
ok
127.0.0.1:6379> set hello big
ok
127.0.0.1:6379> set world big
ok
127.0.0.1:6379> bitop and destkey hello world
(integer) 3
127.0.0.1:6379> bitop or destkey hello world
(integer) 3
127.0.0.1:6379> get destkey
"big"
127.0.0.1:6379> bitop not destkey hello
(integer) 3
127.0.0.1:6379> get destkey
"\x9d\x96\x98"
127.0.0.1:6379> bitop xor destkey hello world
(integer) 3
127.0.0.1:6379> get destkey
"\x00\x00\x00"

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

bitpos key bit [start] [end] (返回位图中第一个值为 bit 的二进制位的位置)

返回字符串里面第一个被设置为1或者0的bit位:

默认情况下整个字符串都会被检索一次,只有在指定start和end参数(指定start和end位是可行的),该范围被解释为一个字节的范围,而不是一系列的位。所以start=0 并且 end=2是指前三个字节范围内查找。

127.0.0.1:6379> flushall
ok
127.0.0.1:6379> set hello big
ok
127.0.0.1:6379> bitpos hello 1
(integer) 1
# 查找字符串里面bit值为0的位置
127.0.0.1:6379> bitpos hello 0
(integer) 0
# 从第1个字节开始的位置,查找字符串里面bit值为0的位置
127.0.0.1:6379> bitpos hello 0 1
(integer) 8
# 第2个字节开始的位置,查找字符串里面bit值为1的位置 
127.0.0.1:6379> bitpos hello 1 2
(integer) 17
# 第0个字节开始到第1个字节结束的位置,查找字符串里面bit值为1的位置
127.0.0.1:6379> bitpos hello 1 0 1
(integer) 1

bitfield key [get type offset] [set type offset value] [incrby type offset increment] [overflow wrap|sat|fail] (该命令将 redis 字符串视为一个位数组,并且能够处理具有不同位宽和任意非(必要)对齐偏移量的特定整数字段。)

bitfield 命令可以将一个 redis 字符串看作是一个由二进制位组成的数组, 并对这个数组中储存的长度不同的整数进行访问 (被储存的整数无需进行对齐)。 换句话说, 通过这个命令, 用户可以执行诸如 “对偏移量 1234 上的 5 位长有符号整数进行设置”、 “获取偏移量 4567 上的 31 位长无符号整数”等操作。 此外, bitfield 命令还可以对指定的整数执行加法操作和减法操作, 并且这些操作可以通过设置妥善地处理计算时出现的溢出情况。

bitfield 命令可以在一次调用中同时对多个位范围进行操作: 它接受一系列待执行的操作作为参数, 并返回一个数组作为回复, 数组中的每个元素就是对应操作的执行结果。

一次对多个位范围进行操作。bitfield 有三个子指令,分别是 get/set/incrby。每个指令都可以对指定片段做操作。

子命令:get —— 返回指定的二进制位范围。
bitfield key get type offset

# 类型u代表无符号十进制,i代表带符号十进制
# 从偏移量offset=0开始取3位,获取无符号整数的值(将前3位二进制011转为无符号10进制返回)
127.0.0.1:6379> bitfield hello get u3 0
1) (integer) 3
# 从偏移量offset=0开始取4位,获取无符号整数的值(将前4位二进制0110转为无符号10进制返回)
127.0.0.1:6379> bitfield hello get u4 0
1) (integer) 6
# 从偏移量offset=0开始取5位,获取无符号整数的值(将前5位二进制01100转为无符号10进制返回)
127.0.0.1:6379> bitfield hello get u5 0
1) (integer) 12
# 从偏移量offset=1开始取2位,获取无符号整数的值(11前面补0,就是0011,转为无符号10进制返回)
127.0.0.1:6379> bitfield hello get u2 1
1) (integer) 3
# 从偏移量offset=0开始取2位,获取带符号整数的值(01前面补0,就是0001,转为带符号10进制返回)
127.0.0.1:6379> bitfield hello get i2 0
1) (integer) 1

子命令:set —— 对指定的二进制位范围进行设置,并返回它的旧值。
bitfield key set type offset value

# 从偏移量offset=0开始取3位,设置为无符号的整数5并返回旧值
127.0.0.1:6379> bitfield hello set u3 0 5
1) (integer) 3
127.0.0.1:6379> bitfield hello get u3 0
1) (integer) 5
# 从偏移量offset=0开始取4位,设置为无符号的整数6并返回旧值
127.0.0.1:6379> bitfield hello set u4 0 6
1) (integer) 10
127.0.0.1:6379> bitfield hello get u4 0
1) (integer) 6
# 从偏移量offset=0开始取4位,设置为带符号的整数5并返回旧值
127.0.0.1:6379> bitfield hello set i4 0 5
1) (integer) 6
127.0.0.1:6379> bitfield hello get i4 0
1) (integer) 5

子命令:incrby —— 对指定的二进制位范围执行加法操作,并返回它的旧值。用户可以通过向 increment 参数传入负值来实现相应的减法操作。
bitfield key incrby type offset increment

# 从偏移量offset=0开始取4位,获取无符号整数的值
# 从偏移量offset=6开始取4位,设置为无符号的整数6 
# 从偏移量offset=4开始取4位,获取无符号整数的值并自增1
127.0.0.1:6379> bitfield hello get u4 0 set u4 4 6 incrby u4 4 1
1) (integer) 5
2) (integer) 2
3) (integer) 7

# 从偏移量offset=0开始取4位,获取无符号整数的值
127.0.0.1:6379>  bitfield hello get u4 0
1) (integer) 5
# 从偏移量offset=4开始取4位,设置为无符号整数6
127.0.0.1:6379>  bitfield hello set u4 4 6
1) (integer) 7
# 从偏移量offset=4开始取4位,获取无符号整数的值并自增1
127.0.0.1:6379> bitfield hello incrby u4 4 1
1) (integer) 7
# 从偏移量offset=4开始取4位,获取无符号整数的值
127.0.0.1:6379> bitfield hello get u4 4
1) (integer) 7
# 从偏移量offset=4开始取4位,获取无符号整数的值并自增1
127.0.0.1:6379> bitfield hello get u4 4 incrby u4 4 1
1) (integer) 7
2) (integer) 8
# 从偏移量offset=0开始取4位,获取无符号整数的值
127.0.0.1:6379> bitfield hello get u4 4
1) (integer) 8
127.0.0.1:6379>

到此这篇关于redis 中的 bitmaps(位图)命令详解的文章就介绍到这了,更多相关redis 位图bitmaps命令内容请搜索代码网以前的文章或继续浏览下面的相关文章希望大家以后多多支持代码网!

(0)

相关文章:

版权声明:本文内容由互联网用户贡献,该文观点仅代表作者本人。本站仅提供信息存储服务,不拥有所有权,不承担相关法律责任。 如发现本站有涉嫌抄袭侵权/违法违规的内容, 请发送邮件至 2386932994@qq.com 举报,一经查实将立刻删除。

发表评论

验证码:
Copyright © 2017-2025  代码网 保留所有权利. 粤ICP备2024248653号
站长QQ:2386932994 | 联系邮箱:2386932994@qq.com