一、BitMap是什么
通過一個bit位來表示某個元素對應的值或者狀態,其中的key就是對應元素本身,value對應0或1,我們知道8個bit可以組成一個Byte,所以bitmap本身會極大的節省儲存空間。
二、Redis中的BitMap
Redis從2.2.0版本開始新增了setbit、
getbit、
bitcount
等幾個bitmap相關命令。雖然是新命令,但是並沒有新增新的數據類型,因為setbit
等命令只不過是在set
上的擴展。
Redis的bitmap讓我們可以實時的進行統計,並且極其節省空間。在模擬1億2千8百萬用戶的模擬環境下,在一台MacBookPro上,典型的統計如“日用戶數”(dailyunique users) 的時間消耗小於50ms, 占用16MB內存。
Bitmap是一串連續的2進制數字(0或1),每一位所在的位置為偏移(offset),在bitmap上可執行AND,OR,XOR以及其它位操作。
三、Redis中的命令
1、setbit
語法:setbit key offset value
描述:
對key所儲存的字符串值,設置或清除指定偏移量上的位(bit)。
位的設置或清除取決於 value
參數,可以是 0
也可以是 1
。
當 key
不存在時,自動生成一個新的字符串值。
字符串會進行伸展(grown)以確保它可以將 value
保存在指定的偏移量上。當字符串值進行伸展時,空白位置以 0
填充。
注意:
offset
參數必須大於或等於 0
,小於 2^32 (bit 映射被限制在 512 MB 之內)。
因為 Redis 字符串的大小被限制在 512 兆(megabytes)以內, 所以用戶能夠使用的最大偏移量為 2^29-1(536870911) , 如果你需要使用比這更大的空間, 請使用多個 key。
當生成一個很長的字符串時, Redis 需要分配內存空間, 該操作有時候可能會造成服務器阻塞(block)。 在2010年出產的Macbook Pro上, 設置偏移量為 536870911(512MB 內存分配)將耗費約 300 毫秒, 設置偏移量為 134217728(128MB 內存分配)將耗費約 80 毫秒, 設置偏移量 33554432(32MB 內存分配)將耗費約 30 毫秒, 設置偏移量為 8388608(8MB 內存分配)將耗費約 8 毫秒。
2、getbit
語法:getbit key offset
描述:
對 key
所儲存的字符串值,獲取指定偏移量上的位(bit)。
當 offset
比字符串值的長度大,或者 key
不存在時,返回 0
3、bitcount
語法:bitcount key [start] [end]
返回值:被設置為 1
的位的數量
描述:
計算給定字符串中,被設置為 1
的比特位的數量
一般情況下,給定的整個字符串都會被進行計數,通過指定額外的 start
或 end
參數,可以讓計數只在特定的位上進行。
start
和 end
參數的設置和 GETRANGE key start end 命令類似,都可以使用負數值: 比如 -1
表示最后一個字節, -2
表示倒數第二個字節,以此類推。
不存在的 key
被當成是空字符串來處理,因此對一個不存在的 key
進行 BITCOUNT
操作,結果為 0
。
4、bitpos
5、bitop
語法:bitop operation destkey key [key ...]
operation
可以是 AND
、 OR
、 NOT
、 XOR
這四種操作中的任意一種:
-
BITOP AND destkey key [key ...]
,對一個或多個key
求邏輯並,並將結果保存到destkey
。BITOP OR destkey key [key ...]
,對一個或多個key
求邏輯或,並將結果保存到destkey
。BITOP XOR destkey key [key ...]
,對一個或多個key
求邏輯異或,並將結果保存到destkey
。BITOP NOT destkey key
,對給定key
求邏輯非,並將結果保存到destkey
。
除了 NOT
操作之外,其他操作都可以接受一個或多個 key
作為輸入。
返回值:保存到 destkey
的字符串的長度,和輸入 key
中最長的字符串長度相等
描述:
對一個或多個保存二進制位的字符串 key
進行位元操作,並將結果保存到 destkey
上。
注意:處理不同長度的字符串
當 BITOP 處理不同長度的字符串時,較短的那個字符串所缺少的部分會被看作 0
。
空的 key
也被看作是包含 0
的字符串序列。
6、bitfield
四、應用場景
1、位圖計數統計
位圖計數統計的是bitmap中值為1的位的個數。位圖計數的效率很高,例如,一個bitmap包含10億個位,90%的位都置為1,在一台MacBook Pro上對其做位圖計數需要21.1ms。
例子:日活躍用戶
為了統計今日登錄的用戶數,我們建立了一個bitmap,每一位標識一個用戶ID。當某個用戶訪問我們的網頁或執行了某個操作,就在bitmap中把標識此用戶的位置為1。
每次用戶登錄時會執行一次redis.setbit(daily_active_users, user_id, 1)。將bitmap中對應位置的位置為1,時間復雜度是O(1)。統計bitmap結果顯示有今天有9個用戶登錄。Bitmap的key是daily_active_users,它的值是1011110100100101。
因為日活躍用戶每天都變化,所以需要每天創建一個新的bitmap。我們簡單地把日期添加到key后面,實現了這個功能。例如要統計某一天有多少個用戶訪問,可以把這個bitmap的key設計為daily_active_users:2019-03-27。當用戶訪問進來,我們只是簡單地在bitmap中把標識這個用戶的位置為1,時間復雜度是O(1)。
備注:
Redis原生指令參考 http://redisdoc.com/index.html
Redis python客戶端 方法參考 http://redis-py.readthedocs.io/en/latest/#indices-and-tables