redis靈魂拷問:聊一聊bitmap使用

bitmap是redis的一種擴展數據類型,主要用於二值狀態統計,好比公司記錄員工打卡記錄,電商網站記錄用戶登陸行爲,積分商城記錄用戶簽到狀況。redis

bigmap底層使用的是String的數據結構,而String保存在計算機中的格式是二進制的字節數組,這樣bitmap就充分利用了每一個字節的bit位,大大節省了內存開銷。數組

下面咱們看一下bitmap的使用。markdown

員工打卡

假如一個公司有100個員工,公司要對員工11月份的打卡行爲進行統計,咱們能夠爲11月份每一天分配一個bitmap,這個bitmap保存100個bit位,來記錄員工的打卡行爲。數據結構

注意:bitmap偏移量從0開始,因此100個bit位是從0~99,依次記錄1-100號員工。

咱們定義bitmap的key格式爲:signed:20201101,記錄2020年11月1日的打卡狀況。下面代碼是員工打卡和查詢員工打卡狀況:ide

/**
 * SETBIT命令
 * 員工打卡
 * 時間複雜度:O(1)
 */
public void sign(String key, int employeeNumber){
    redisTemplate.opsForValue().setBit(key, employeeNumber - 1, true);
}

/**
 * GETBIT命令
 * 查看員工打卡狀況
 * 時間複雜度:O(1)
 */
public boolean isSigned(String key,int employeeNumber){
    return redisTemplate.opsForValue().getBit(key, employeeNumber - 1);
}

咱們能夠查看某一天的打卡總人數,代碼以下,入參:"signed:20201101":測試

/**
 * BITCOUNT命令
 * 查看某一天的打卡人數
 * 時間複雜度:O(N)
 */
public Long signedCount(String key){
    return (Long) redisTemplate.execute((RedisCallback<Long>)  conn -> conn.bitCount(key.getBytes()));
}

這樣咱們就能根據打卡人數來判斷當天的遲到人數比例。網站

注意:上面的sign方法必須設置key的序列化採用StringRedisSerializer,不然查詢打卡狀況是查不到的。若是不設置StringRedisSerializer,上面的sign和isSigned改成使用conn來執行,代碼以下:code

public void sign(String key, int employeeNumber){
    redisTemplate.execute((RedisCallback<Boolean>)  conn -> conn.setBit(key.getBytes(), employeeNumber - 1, true));
}
public boolean isSigned(String key, int employeeNumber){
    return redisTemplate.execute((RedisCallback<Boolean>)  conn -> conn.getBit(key.getBytes(), employeeNumber - 1));
}

或者使用下面代碼來設置RedisTemplate的setKeySerializer:內存

redisTemplate.setKeySerializer(new StringRedisSerializer());

那若是想看當月沒有遲到過的員工呢?這個時候就要用到交集了,對當月天天的bitmap作交集,值爲1的員工就是沒有遲到過的。get

這時就要用到bitmap的聚合運算了,命令BITOP, 支持AND(與)、OR(或), XOR(異或) and NOT(非)運算,除了NOT後面跟一個bitmap外,其餘3種聚合運算後面均可以跟多個bitmap,命令以下:

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

爲了讓demo簡單一些,我這裏給出一個查看2天內沒有遲到的員工,代碼以下:

/**
 * 命令:BITOP
 * 複雜度:O(N)
 * 整個月全勤的員工數量,這裏用2天表明整個月
 * @param key1 第一天
 * @param key2 次日
 */
public Long signedAllMonth(String key1, String key2){
    String andMap = "signedAllMonth11";
    redisTemplate.execute((RedisCallback)  conn -> conn.bitOp(RedisStringCommands.BitOperation.AND, andMap.getBytes(), key1.getBytes(), key2.getBytes()));
    return (Long) redisTemplate.execute((RedisCallback<Long>)  conn -> conn.bitCount(andMap.getBytes()));
}

下面我再給出一段測試代碼,這段代碼模擬有50個員工全勤,bitMapService是我上面的代碼所在類:

@Test
public void testSignedAllMonth(){
    for (int i = 1; i <= 100; i++){
        bitMapService.sign("signed:20201101", i);
    }
    for (int i = 1; i <= 100; i += 2){
        bitMapService.sign("signed:20201102", i);
    }
    Long count = bitMapService.signedAllMonth("signed:20201101", "signed:20201102");
    System.out.println("=========="+count);
}

輸出以下:

==========50

判斷日活躍用戶數量

好比網站有10萬個用戶,咱們要判斷當天的日活用戶。這樣咱們建立一個長度爲10萬的bitmap,每一個用戶id佔一個位,咱們定義key爲:user:login,number爲用戶編號。當有用戶登陸時,調用下面的方法:

redisTemplate.opsForValue().setBit(key, number - 1, true);

日終的時候,咱們用下面的方法就能夠判斷出日活用戶:

redisTemplate.execute((RedisCallback<Long>)  conn -> conn.bitCount(key.getBytes()));

總結

bitmap普遍地運用在二值計算的場景,對於一個二值狀態只用一個bit位就能夠,很是節約內存。好比咱們對一個10億的用戶進行日活計算,佔用的空間只有120M:

10億/8/1024/1024=120M

官網連接:

https://redis.io/commands/bitop

感謝閱讀,歡迎點贊,歡迎在看,更歡迎分享給須要的朋友。

相關文章
相關標籤/搜索