Redis 中 BitMap 的使用場景

程式設計師自由之路發表於2020-10-12

BitMap

BitMap 原本的含義是用一個位元位來對映某個元素的狀態。由於一個位元位只能表示 0 和 1 兩種狀態,所以 BitMap 能對映的狀態有限,但是使用位元位的優勢是能大量的節省記憶體空間。

在 Redis 中,可以把 Bitmaps 想象成一個以位元位為單位的陣列,陣列的每個單元只能儲存0和1,陣列的下標在 Bitmaps 中叫做偏移量。

需要注意的是:BitMap 在 Redis 中並不是一個新的資料型別,其底層是 Redis 實現。

BitMap 相關命令

# 設定值,其中value只能是 0 和 1
setbit key offset value

# 獲取值
getbit key offset

# 獲取指定範圍內值為 1 的個數
# start 和 end 以位元組為單位
bitcount key start end

# BitMap間的運算
# operations 位移操作符,列舉值
  AND 與運算 &
  OR 或運算 |
  XOR 異或 ^
  NOT 取反 ~
# result 計算的結果,會儲存在該key中
# key1 … keyn 參與運算的key,可以有多個,空格分割,not運算只能一個key
# 當 BITOP 處理不同長度的字串時,較短的那個字串所缺少的部分會被看作 0。返回值是儲存到 destkey 的字串的長度(以位元組byte為單位),和輸入 key 中最長的字串長度相等。
bitop [operations] [result] [key1] [keyn…]

# 返回指定key中第一次出現指定value(0/1)的位置
bitpos [key] [value]

BitMap 佔用的空間

在弄清 BitMap 到底佔用多大的空間之前,我們再來重申下:Redis 其實只支援 5 種資料型別,並沒有 BitMap 這種型別,BitMap 底層是基於 Redis 的字串型別實現的。

我們通過下面的命令來看下 BitMap 佔用的空間大小:

# 首先將偏移量是0的位置設為1
127.0.0.1:6379> setbit csx:key:1 0 1
(integer) 0
# 通過STRLEN命令,我們可以看到字串的長度是1
127.0.0.1:6379> STRLEN csx:key:1
(integer) 1
# 將偏移量是1的位置設定為1
127.0.0.1:6379> setbit csx:key:1 1 1
(integer) 0
# 此時字串的長度還是為1,以為一個字串有8個位元位,不需要再開闢新的記憶體空間
127.0.0.1:6379> STRLEN csx:key:1
(integer) 1
# 將偏移量是8的位置設定成1
127.0.0.1:6379> setbit csx:key:1 8 1
(integer) 0
# 此時字串的長度程式設計2,因為一個位元組存不下9個位元位,需要再開闢一個位元組的空間
127.0.0.1:6379> STRLEN csx:key:1
(integer) 2

通過上面的實驗我們可以看出,BitMap 佔用的空間,就是底層字串佔用的空間。假如 BitMap 偏移量的最大值是 OFFSET_MAX,那麼它底層佔用的空間就是:

(OFFSET_MAX/8)+1 = 佔用位元組數

因為字串記憶體只能以位元組分配,所以上面的單位是位元組。

但是需要注意,Redis 中字串的最大長度是 512M,所以 BitMap 的 offset 值也是有上限的,其最大值是:

8 * 1024 * 1024 * 512  =  2^32

由於 C語言中字串的末尾都要儲存一位分隔符,所以實際上 BitMap 的 offset 值上限是:

(8 * 1024 * 1024 * 512) -1  =  2^32 - 1

使用場景

1. 使用者簽到

很多網站都提供了簽到功能,並且需要展示最近一個月的簽到情況,這種情況可以使用 BitMap 來實現。
根據日期 offset = (今天是一年中的第幾天) % (今年的天數),key = 年份:使用者id。

如果需要將使用者的詳細簽到資訊入庫的話,可以考慮使用一個一步執行緒來完成。

2. 統計活躍使用者(使用者登陸情況)

使用日期作為 key,然後使用者 id 為 offset,如果當日活躍過就設定為1。具體怎麼樣才算活躍這個標準大家可以自己指定。

假如 20201009 活躍使用者情況是: [1,0,1,1,0]
20201010 活躍使用者情況是 :[ 1,1,0,1,0 ]

統計連續兩天活躍的使用者總數:

bitop and dest1 20201009 20201010 
# dest1 中值為1的offset,就是連續兩天活躍使用者的ID
bitcount dest1

統計20201009 ~ 20201010 活躍過的使用者:

bitop or dest2 20201009 20201010 

3. 統計使用者是否線上

如果需要提供一個查詢當前使用者是否線上的介面,也可以考慮使用 BitMap 。即節約空間效率又高,只需要一個 key,然後使用者 id 為 offset,如果線上就設定為 1,不線上就設定為 0。

4. 實現布隆過濾器

參考

相關文章