探究Redis 06:位图与计数器
位图(Bitmap)
Redis位图并非一种独立的数据类型,而是基于字符串数据类型定义的一组位运算操作。由于字符串被定义为一个最大长度512MB的二进制操作安全的数据块, 位图最大支持存储2的32次方个比特位。
Redis支持两类比特操作:
- 固定时长单比特操作,例如:设置某个比特位为0或1,或者获取对应值
- 批量比特操作,例如:返回一个范围内的比特被设置的数量操作。
比特操作的最大优势是通常情况下,可以提供最大的存储空间利用率。例如:在一个系统中,不同的用户通过递增ID来进行区分,利用512 MB 位图,可以很容易的记录40亿个用户是否订阅了新闻组功能。
位图通过 SETBIT 和 GETBIT 命令设置和获取位信息:
> setbit key 10 1
(integer) 1
> getbit key 10
(integer) 1
> getbit key 11
(integer) 0
SETBIT 命令的第一个参数为比特位偏移位置, 第二个参数是bit位内容(0或1)。此命令可根据偏移位置大小,自动扩充当前字符串长度。
GETBIT 命令支持从特定偏移位置返回比特位。如果参数指定了超过当前实际存储范围的偏移位置,默认会返回0。
批量比特位操作包括以下三种:
- BITOP 在不同字符串之间进行比特位运算. 当前支持的运算包括 AND, OR, XOR 和 NOT.
- BITCOUNT 此命令返回位图信息中,值为1的个数。
- BITPOS 此命令可以查找第一个出现0或1的位置。
BITPOS 和BITCOUNT 都支持从字符串的某个位置范围进行查找, 相比整个字符串查找,效率更高。
以下是BITCOUNT 命令的简单示例:
> setbit key 0 1
(integer) 0
> setbit key 100 1
(integer) 0
> bitcount key
(integer) 2
位图的常见使用场景包括:
- 各类数据的实时分析
- 基于对象ID的高效布尔信息存储
例如,假设您想知道网站用户最长连续访问天数。可以从网站发布的第0天开始, 通过SETBIT 命令记录用户每次访问网站的日期(1比特)。比特偏移位置可以通过获取当前unixtime减去开始时间除以3600*24简单获取。
通过这种方式,对于每一个用户可以通过一个小字符串记录每日访问。通过BITCOUNT命令,可以很容易获取某个用户访问网站的天数。同时通过BITPOS 命令或直接在客户端分析整个位图数据, 很容易计算出最长连续访问天数。
位图很容易拆分为多个key, 例如:为了拆分大数据集合。拆分的一种简单的策略是,每个key存储M比特,通过bit-number/M计算key名称,通过bit-number MOD M计算数据在key中的相对位置。
计数器(HyperLogLog )
Redis计数器是一种基于统计概率模型的数据结构,用于对一组元素进行唯一性计数。通常情况下,统计一组唯一元素需要耗费与元素个数成正比例的存储容量。原因是为了避免重复计数,需要记录出现过的每个不同的元素个体。
然而通过一些算法组合,存在一种牺牲部分精准度换取内存空间的方法。
在这种算法的Redis实现中,计数器返回一个估计统计值,并保证误差小于1%。同时,所消耗的内存最高不超过12k 字节。在实际使用中,如果统计的元素数量不多,所消耗的内存将远远小于这个上限。
计数器(HLL)在Redis中虽然是一个特殊的数据结构,但是存储时,通过序列化为字符串保存,因此你可以使用GET 或 SET 命令获取和保存计数器。
概念上使用计数器类似集合操作。你可以各种元素SADD 到集合中,由于SADD 不会重复添加同样的数据,之后通过SCARD 可以获取集合中独立元素的数量。
请记住在计数器中,你不是真的将元素添加到计数器中,而是仅仅保存了元素统计的中间状态。
-
每次通过 PFADD命令向计数器添加元素
-
每次需要估计通过PFADD记录的独立元素的个数,可以通过PFCOUNT命令。
> pfadd hll a b c d (integer) 1 > pfcount hll (integer) 4
使用计数器的一个场景示例是通过计数器统计用户每天在搜索表单中的独立查询数量。