bitmap的底层数据结构就是0/1bit 的二进制数值,最大长度512Mb = 512 * 1024 * 1024 * 8 = 2^32 。使用场景举例:
- 用户登录活跃值记录
- 打卡签到记录,key为用户唯一键,offset是由日期换算得出的数值,1为已打卡,0为未打卡
- 热点活动记录,key为热点事件,offset是每个用户唯一键例如(id=12385123),如果该用户参与了此次活动,则在12385123的bit位设为1
- …
1.位图常用命令
- SETBIT命令
用来设置或者清除某一位上的值,其返回值是原来位上存储的值。key 在初始状态下所有的位都为 0 ,语法格式如下:
SETBIT key offset value
其中 offset 表示偏移量,从 0 开始。示例如下:
#设置0位置的值为1
127.0.0.1:6379> SETBIT test 0 1
(integer) 1
- GETBIT命令
用来获取某一位上的值。
GETBIT key offset
其中 offset 表示偏移量,从 0 开始。示例如下:
#设置0位置的值为1
127.0.0.1:6379> GETBIT test 0
(integer) 1
#当偏移量 offset 比字符串的长度大,或者当 key 不存在时,返回 0。
127.0.0.1:6379> GETBIT bits 100000
(integer) 0
- BITCOUNT命令
统计指定位区间上,值为 1 的个数。语法格式如下:
BITCOUNT key [start end]
示例如下:
127.0.0.1:6379> BITCOUNT test
(integer) 8
通过指定的 start 和 end 参数,可以让计数只在特定的字节上进行。start 和 end 参数可以使用负数,比如 -1 表示倒数第一个位, -2 表示倒数第二个位。不指定的话默认全部
当偏移量 offset 比字符串的长度大,或者当 key 不存在时,返回 0。
- BITFIELD命令
下面是官网对其子命令的一些解释:
这里解释下:
- type为类型,可以是 u/i 两个字符,代表无符号和有符号的区别
- type后跟的数字表示需要查询的范围位数,这里需要重点标注,比如u6指的就是无符号只查询6位的十进制结果
- offset 就是偏移量,从哪个位置开始查找,0代表第一位
这里画个图更容易理解一下,我们存储的结构好似一个游标,u6指的就是我只查询6个bit单位的部分数据,offset指的是从哪里开始查,下图示为u4,offset为1:
BITFIELD key GET type offset
下面示例:我这里提前对test的0,1,2,3,4,5 分别设置了上图一样的数值:1,0,1,1,1,1。取u4 offset为0,值为11,就是把游标框中的二进制0111拿出来转为十进制。
这里有个规律,所有位置默认均为0bit,如果选择的是u4 offset为5,那么就是从下标5开始向后4位bit范围数值,取出的值为1000
127.0.0.1:6379> BITFIELD test get u4 0
1) (integer) 11
- EXISTS命令
判断是否有该key,存在返回1,不存在返回0
127.0.0.1:6379> EXISTS test
(integer) 1
127.0.0.1:6379> EXISTS test1
(integer) 0
2.布隆过滤器原理
在防止缓存穿透的时候经常提到布隆过滤器,布隆过滤器就是基于bitmap来实现的,下面大概说一下实现的原理。
它实际上是一个很长的二进制向量和一系列随机映射函数。布隆过滤器可以用于检索一个元素是否在一个集合中。它的优点是空间效率和查询时间都比一般的算法要好的多,缺点是有一定的误识别率和删除困难。
布隆过滤器的优点:
- 时间复杂度低,增加和查询元素的时间复杂为O(N),(N为哈希函数的个数,通常情况比较小)
- 保密性强,布隆过滤器不存储元素本身
- 存储空间小,如果允许存在一定的误判,布隆过滤器是非常节省空间的(相比其他数据结构如Set集合)
布隆过滤器的缺点:
- 有点一定的误判率,但是可以通过调整参数来降低
- 无法获取元素本身
- 很难删除元素
数据结构
布隆过滤器它实际上是一个很长的二进制向量和一系列随机映射函数。以Redis中的布隆过滤器实现为例,Redis中的布隆过滤器底层是一个大型位数组(二进制数组)+多个无偏hash函数。
一个大型位数组(二进制数组):
多个无偏hash函数:
无偏hash函数就是能把元素的hash值计算的比较均匀的hash函数,能使得计算后的元素下标比较均匀的映射到位数组中。
如下就是一个简单的布隆过滤器示意图,其中k1、k2代表增加的元素,a、b、c即为无偏hash函数,最下层则为二进制数组。
增加元素的步骤:
- 通过k个无偏hash函数计算得到k个hash值
- 依次取模数组长度,得到数组索引
- 将计算得到的数组索引下标位置数据修改为1
查询元素的步骤:
布隆过滤器最大的用处就在于判断某样东西一定不存在或者可能存在,而这个就是查询元素的结果。其查询元素的过程如下:
- 通过k个无偏hash函数计算得到k个hash值
- 依次取模数组长度,得到数组索引
- 判断索引处的值是否全部为1,如果全部为1则存在(这种存在可能是误判),如果存在一个0则必定不存在
误判的情况: hash函数无法完全避免hash冲突,可能会存在多个元素计算的hash值是相同的,那么它们取模数组长度后的到的数组索引也是相同的,这就是误判的原因。例如彭于晏和程序员(莫打我)的hash值取模后得到的数组索引都是1,但实际上存储的只有彭于晏,如果此时判断程序员在不在这里,误判就出现啦!因此布隆过滤器最大的缺点误判只要知道其判断元素是否存在的原理就很容易明白了!
布隆过滤器不支持删除元素。