Redis BITCOUNT 命令
语法
1 | BITCOUNT key [start end [BYTE | BIT]] |
可用版本
≥ 2.6.0
时间复杂度
$O(N)$
ACL类别
@read, @bitmap, @slow
计算给定字符串中,被设置为 1 的比特位的数量。
默认情况下,给定的整个字符串都会被进行计数,通过指定额外的 start 和 end 参数,可以让计数只在特定的位上进行。
start 和 end 参数的设置和 GETRANGE key start end 命令类似,都可以使用负数值: 比如 -1 表示最后一个字节,-2 表示倒数第二个字节,以此类推。
不存在的 key 被当成是空字符串来处理,因此对一个不存在的 key 进行 BITCOUNT 操作,结果为 0 。
默认情况下,参数 start 和 end 指定一个字节索引。我们可以使用一个附加参数 BIT 来指定一个比特索引。所以 0 是第一位,1 是第二位,以此类推。对于负值,-1 是最后一位,-2 是倒数第二位,依此类推。
返回值
返回一个整数,表示被设置为 1 的位的数量。
示例 1
对一个不存在的 key 进行 BITCOUNT 操作:
1 | redis> EXISTS bits_key |
示例 2
对存储值的类型不是字符串的 key 进行 BITCOUNT 操作:
1 | redis> TYPE list_key |
示例 3
对存储值为字符串 JOHNSON LIN 的 key 进行 BITCOUNT 操作:
1 | redis> GET user |
字符串 JOHNSON LIN 的二进制表示:
1 | 01001010 01001111 01001000 01001110 01010011 01001111 01001110 00100000 01001100 01001001 01001110 |
示例 4
通过指定 start 和 end 参数,只对存储值的第一个字节、第二个字节进行计数:
1 | redis> BITCOUNT user 0 1 BYTE |
该命令也可以省略 BYTE 参数:
1 | redis> BITCOUNT user 0 1 |
示例 5
通过指定 start 和 end 参数,只对存储值的倒数第一个字节、倒数第二个字节进行计数:
1 | redis> BITCOUNT user -2 -1 BYTE |
省略 BYTE 参数:
1 | redis> BITCOUNT user -2 -1 |
示例 6
通过指定 start 和 end 参数,只对存储值的第一位、第二位进行计数:
1 | redis> BITCOUNT user 0 1 BIT |
示例 7
通过指定 start 和 end 参数,只对存储值的倒数第八位至倒数第一位进行计数:
1 | redis> BITCOUNT user -8 -1 BIT |
模式:使用 Bitmap 存储实时指标
对某些特定类型的数据,使用 Bitmap 存储可以非常节省空间。例如,记录 Web 应用用户访问历史以筛选参与新功能测试的用户。
使用 SETBIT 命令可以轻松实现这一功能。可以使用一个小的递增整数来代表每一天,如 0 代表应用上线首日,1 代表第二日,以此类推。
每次用户浏览页面时,应用可使用 SETBIT 命令设置与当前日期对应的位,记录用户在当日访问。之后只需对 Bitmap 调用 BITCOUNT 命令就可以轻松知道用户访问的总天数。
一篇名为“Fast easy realtime metrics using Redis bitmaps”的文章描述了类似方式,只是使用用户 ID 而非日期。
性能考虑因素
在上述计算用户访问天数的示例中,即使应用运行 10 年,每个用户的数据也仅为 365*10 比特,即每个用户只需 456 字节。对这样的数据量,BITCOUNT 的速度仍然与其他 $O(1)$ Redis 命令一样快,如 GET 或 INCR。
当位图很大时,有两种选择:
将一个大的 Bitmap 分散到不同的 key 中,作为小的 Bitmap 来处理。使用 Lua 脚本可以高效且原子地实现这一功能。
使用
BITCOUNT的start和end参数,遍历位图,每次只对部分位进行计算,然后在客户端累加结果。我们可以选择将累加结果缓存至一个键。
历史记录
从 Redis 7.0.0 版本开始:增加了 BYTE、BIT 选项。
(END)