redis的bitset实战

本文主要研究一下redis的bitset数据结构的用场java

相关命令

SETBIT

时间复杂度为O(1)git

setbit login.20180906 102400000 0
setbit login.20180905 201400000 1

GETBIT

时间复杂度为O(1)github

getbit login.20180905 201400000

BITOP

时间复杂度为O(N)redis

bitop or login.9m.week1or login.20180905 login.20180906
getbit login.9m.week1or 201400000
主要作bitset的and、or、xor、not操做,结果存在新的bitset中,注意时间复杂度为O(N)

BITPOS

时间复杂度为O(N)数据结构

bitpos login.20180905 1
返回指定bitset中在指定起始位置中第一个出现指定值的offset,不传start,end默认估计是0,-1

BITCOUNT

时间复杂度为O(N)app

bitcount login.20180905
统计bitset中出现1的个数

使用场景

假设有个签到的需求,要实现的功能以下:ui

  • 展现当天是否已经签到,签到了不能再签到了
  • 展现最近一周的或者最近一个月的签到状况/历史(能够只不详细记录到天天的签到时间,只记录天天是否签到)
  • 判断是否连续签到,若本周连续签到,则给予抽奖机会

这里咱们就可使用redis的bitset来实现:code

签到

boolean originValue = redisTemplate.opsForValue().setBit(uidYearKey,dayIndx,true);
  • 这里的key由uid,year构成,而后offset采用day的index
  • 每一个uid每一个year一个key的话,若是用户数过多可能形成redis的key太多

获取签到数据

BitSet bitSet = fromByteArrayReverse(redisTemplate.opsForValue().get(uidYearKey).getBytes());
    public static BitSet fromByteArrayReverse(final byte[] bytes) {
        final BitSet bits = new BitSet();
        for (int i = 0; i < bytes.length * 8; i++) {
            if ((bytes[i / 8] & (1 << (7 - (i % 8)))) != 0) {
                bits.set(i);
            }
        }
        return bits;
    }
  • 这里有个注意事项,java读取bytes从小到大是从右往左读(大端),而redis存储的bytes从小到大是从左往右(小端),于是这里读取bytes转为BitSet须要逆向一下

BitSet Range

public BitSet get(int fromIndex, int toIndex) {
    //......
}
  • BitSet有个方法,能够根据index来进行range,以后就能够用新的BitSet进行相关统计,好比BitSet的cardinality

小结

  • 对于bitset来讲,其优势就是节省内存,若是直接把用户id做为offset来存储相应的值,这个相比hash来讲,节省了不少空间。相似统计最近N天连续登录的人的个数这类场景就可使用bitset来实现。
  • 对于bitset的操做要注意,各个操做的时间复杂度,若是是getbit、setbit则都是O(1),bitop、bitcount、bitpos等都是O(N),在N比较大的时候要注意,多是潜在的慢查询

doc

相关文章
相关标签/搜索