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

相關文章
相關標籤/搜索