Redis允许使用二进制数据的Key(binary keys) 和二进制数据的Value(binary values)。Bitmap就是二进制数据的value。Redis的 setbit(key, offset, value)操作对指定的key的value的指定偏移(offset)的位置1或0,时间复杂度是O(1)。
Redis BitMaps的操作命令:
- SETBIT key offset value
对指定的key的value的指定偏移(offset)的位置1或0 - GETBIT key offset
获取offset设置的值,未设置过默认返回0 - BITCOUNT key [start end]
统计指定key位置为1的数量(区间统计不建议使用,bitcount用的是byte来计算位数,其他setbit和getbit用的是bit,详见:http://blog.linuxphp.org/archives/1627/) - BITOP operation destkey key [key ...]
Bit运算,BITOP 支持四种表达式运算: AND(交集), OR(并集), XOR(异或) 和NOT(取非), 用法如下:
BITOP AND destkey srckey1 srckey2 srckey3 ... srckeyN
BITOP OR destkey srckey1 srckey2 srckey3 ... srckeyN
BITOP XOR destkey srckey1 srckey2 srckey3 ... srckeyN
BITOP NOT destkey srckey
- BITPOS key bit [start] [end]
返回设置为1或0的一个字符串中的第一个点的位置
- 活跃用户统计
活跃用户是项目中经常需要统计的一个项目,我们之前的做法是:
- 直接从用户的登录日志中计算提取 (需要脚本定时计算,比较耗时)
- 将每天的登录用户的uid存在Redis的SET结构中 (用户基数大的情况下,开销大)
以上做法固然没有什么问题,但是当用户规模特别大的时候,就显得笨拙或者开销比较大,于是Redis bitmaps就上场了。
为统计某一天的用户登陆数量,首先以当天的日志加固定的前缀作为key,建立一个bitmap。例如:userlogin:2016-01-15,每一位二进制的位做为一个用户ID的标识(offset)。当用户访问时,就把bitmap中标识此用户的二进制(value)从0置为1。
用户登录时执行一次SETBIT(key, userId, 1),将bitmap中对应位置的位置为1。当统计某一天用户活跃数时,直接bitcount那天对应的key,当然还可以用bitop计算出如最近7天,一月个的用户活跃数。代码如下:
2. 用户行为统计
网站一般经常有个场景就是,判断用户是否操作过某个动作,比如是否点击过某个按钮(根据是否点击改变按钮颜色),是否领取过优惠券(是否领取过展示逻辑不同)。
这些场景其实也可以运用到bitMap,但是前提是不大关注行为的人,因为bitMap并不能计算出用户的uid。
之所以选择用bitmap,就是因为bitmap的操作非常快,而且也比较节省资源。
扩展下,其实如点赞喜欢的判断逻辑也可以用bitmap来实现,当然关系逻辑还是存在mysql中(因为这些关系有时候很重要),用bitmap来加速判断逻辑。
最后,说一下bitmap的两个容易采坑的地方:
- 第一个,就是bitcount命令,在使用start,end的时候一定要注意,setbit和getbit命令操作的是bit,但是bitcount用的是byte来计算位数,两者差了8倍,因此这点很容易采坑,也不建议用。
- setbit的offset是用大小限制的,在0到 232(最大使用512M内存)之间,即0~4294967296之前,超过这个数会自动将offset转化为0,因此使用的时候一定要注意。