Redis中的位圖
Redis中的位圖是由多個二進制位組成的數組,數組中每個二進制位都有與之對應的偏移量(索引),用戶通過索引可以對位圖中制定的一個或者多個二進制位進行操作。
通過位圖我們可以:
- 為位圖指定偏移量上的二進制位設置值,或者獲取位圖指定偏移量上的二進制位的值。
- 統計位圖中有多少二進制位被設置成了1。
- 查找位圖中第一個被設置為指定值的二進制位並返回它的偏移量。
- 對一個或者多個位圖執行邏輯並、邏輯或、邏輯異或以及邏輯非運算。
- 將指定類型的整數存儲到位圖中。
1. SETBIT:設置二進制位的值
SETBIT bitmap offset value
SETBIT命令在對二進制位進行設置之后,將返回二進制位被設置之前的舊值作為結果。當用戶所設置的位圖不存在或者位圖當前的大小無法滿足用戶想要執行的設置操作,那么Redis將對被設置的位圖進行擴展滿足設置要求。
因為Redis對位圖的擴展操作是以字節為單位進行的,所以擴展之后的位圖包含的二進制位數量可能會比命令中要求的多,並且在擴展位圖的同時,Redis還會將所有未被設置的二進制位的值初始化為0。
SETBIT命令只能使用正數偏移量,嘗試使用負數將會引發錯誤。
其他信息
-
復雜度:O(1)。
-
版本要求:SETBIT命令從Redis 2.2.0版本開始可用。
2. GETBIT:獲取二進制位的值
GETBIT bitmap offset
GETBIT命令可以獲取位圖指定偏移量的值,並且也只接受正數作為偏移量。如果用戶輸入的偏移量超過了位圖目前擁有的最大偏移量,那么該命令將返回0作為結果
其他信息
-
復雜度:O(1)。
-
版本要求:GETBIT命令從Redis 2.2.0版本開始可用。
3. BITCOUNT:統計被設置的二進制位數量
BITCOUNT bitmap [start end]
用戶可以執行BITCOUNT命令統計位圖中值為1的二進制位數量,用戶也可以通過可選的start參數和end參數,讓BITCOUNR只對指定字節范圍內對二進制位進行統計。
這里需要注意start和end參數是用來指定偏移量而不是二進制位偏移量的。這些參數還可以是負數索引。
其他信息
-
復雜度:O(N),其中N為被統計字節的數量。
-
版本要求:BITCOUNT命令從Redis 2.6.0版本開始可用。
4. BITPOS:查找第一個指定的二進制位值
BITPOS bitmap value [start end]
用戶可以使用該命令,在位圖中查找第一個被設置為指定值的二進制位,並返回這個二進制位的偏移量。也可以在指定的字節返回內查找。同樣可以使用負數索引。
當用戶嘗試對一個不存在的位圖或者一個所有位都設置成了0的位圖中查找值為1 的二進制位時,BITPOS命令將返回-1作為結果。
如果用戶在一個所有位都被設置成1的位圖中查找值為0的二進制位,那么BITPOS命令將返回位圖最大偏移量加上1作為結果。
其他信息
-
復雜度:O(N),其中N為查找涉及的字節數量。
-
版本要求:BITPOS命令從Redis 2.8.7版本開始可用。
5. BITOP:執行二進制位運算
BITOP operation result_key bitmap [bitmap ...]
用戶可以同過該命令,對一個或者多個位圖執行指定的二進制位運算,並將運算結果存儲到指定的鍵中,operation參數的值可以是AND、OR、XOR、NOT中的任意一個,當BITOP命令在對兩個不同長度的位圖執行運算時,會將長度較短的那個位圖中不存在的二進制位的值看作0。
其他信息
-
復雜度:O(N),其中N為計算涉及的字節總數量。
-
版本要求:BITOP命令從Redis 2.6.0版本開始可用。
6. BITFIELD:在位圖中存儲整數值
“在一般情況下,當用戶使用字符串或者散列去存儲整數的時候,Redis都會為被存儲的整數分配一個long類型的值(通常為32位長或者64位長),並使用對象去包裹這個值,然后再把對象關聯到數據庫或者散列中。
與此相反,BITFIELD命令允許用戶自行指定被存儲整數的類型,並且不會使用對象去包裹這些整數,因此當我們想要存儲長度比long類型短的整數,並且希望盡可能地減少對象包裹帶來的內存消耗時,就可以考慮使用位圖來存儲整數。”
BITFIELD命令允許用戶在位圖中的任意區域field存儲指定長度的整數值,並對這些整數值執行加法或者減法操作。
1. 根據偏移量對區域進行設置
通過使用BITFIELD命令的SET子命令,用戶可以在位圖的指定偏移量offset(標識第n個二進制位,從0開始)上設置一個type類型的整數值vlaue:
BITFIELD bitmap SET type offset value
- type參數用於指定被設置值的類型,這個參數的值需要以i或者u為前綴,后跟被設置值的位長度,其中i表示被設置的值為有符號整數,而u則表示被設置的值為無符號整數。比如i8表示被設置的值為有符號8位整數,而u16則表示被設置的值為無符號16位整數,諸如此類。BITFIELD的各個子命令目前最大能夠對64位長的有符號整數(i64)和63位長的無符號整數(u63)進行操作。
- value參數用於指定被設置的整數值,這個值的類型應該和type參數指定的類型一致。如果給定值的長度超過了type參數指定的類型,那么SET命令將根據type參數指定的類型截斷給定值。比如,如果用戶嘗試將整數123(二進制表示為01111011)存儲到一個u4類型的區域中,那么命令會先將該值截斷為4位長的二進制數字1011(即十進制數字11),然后再進行設置。
2. 根據索引對區域進行設置
SET子命令還允許用戶根據給定類型的位長度,對位圖在指定索引上存儲的整數值進行設置:
BITFIELD bitmap SET type #index value
當位圖中存儲的都是相同類型的整數值時,使用這種設置方法將給用戶帶來非常大的便利,因為這種方法允許用戶直接對位圖指定索引上的整數值進行設置,而不必知道這個整數值具體存儲在位圖的哪個偏移量上。
假設現在有一個位圖,它存儲着多個8位長的無符號整數,而我們想要把它的第133個8位無符號整數的值設置為22。如果使用SET子命令的偏移量設置格式,就需要先使用算式(133-1)*8計算出第133個8位無符號整數在位圖中的起始偏移量1056,然后再執行以下命令:
BITFIELD bitmap SET u8 1056 22
很明顯,這種手動計算偏移量然后進行設置的做法非常麻煩也很容易出錯。與此相反,如果我們使用的是SET子命令的索引設置格式,那么只需要執行以下命令就可以對位圖的第133個無符號整數進行設置了。
3. 獲取區域存儲的值
通過GET子命令,用戶可以從給定偏移量或者索引中取出指定類型的整數值:
BITFIELD bitmap GET type offset
BITFIELD birmap GET type #index
各個參數與SET命令基本一致,當給定的索引或者偏移量超出了位圖的邊界,那么GET子命令將返回0作為結果。
4. 執行加法操作活着減法操作
通過INCRBY子命令,可以實現對位圖存儲的整數值執行加法操作或者減法操作。通過傳入對應的負數增量來達到執行減法操作的效果。
BITFIBLD bitmap INCEBY type offset increment
BITFIBLD bitmap INCEBY type #index increment
5. 處理溢出
使用OVERFLOW子命令去控制INCRBY子命令在計算時發生溢出的行為。
BITFIBLD bitmap [...] OVERFLOW WROP|SAT|FAIL [...]
WRAP表示使用回繞(wrap around)方式處理溢出,這也是C語言默認的溢出處理方式。在這一模式下,向上溢出的整數值將從類型的最小值開始重新計算,而向下溢出的整數值則會從類型的最大值開始重新計算。
SAT表示使用飽和運算(saturation arithmetic)方式處理溢出,在這一模式下,向上溢出的整數值將被設置為類型的最大值,而向下溢出的整數值則會被設置為類型的最小值。
FAIL表示讓INCRBY子命令在檢測到計算會引發溢出時拒絕執行計算,並返回空值表示計算失敗.
OVERFLOW子命令在執行時將不產生任何回復。此外,如果用戶在執行BITFIELD命令時沒有指定具體的溢出處理方式,那么INCRBY子命令默認使用WRAP方式處理計算溢出。
OVERFLOW子命令只會對同一個BITFIELD調用中排在它之后的那些INCRBY子命令產生效果,所以用戶必須把OVERFLOW子命令放到它想要影響的INCRBY子命令之前。
其他信息
-
復雜度:O(N),其中N為用戶給定的子命令數量。
-
版本要求:BITFIELD命令從Redis 3.2.0版本開始可用。
總結
-
Redis的位圖是由多個二進制位組成的數組,數組中的每個二進制位都有與之對應的偏移量(也稱索引),用戶通過這些偏移量可以對位圖中指定的一個或多個二進制位進行操作。
-
BITCOUNT命令接受的是字節索引范圍,而不是二進制位索引范圍,忽略這一點很容易引發程序錯誤。
-
BITFIELD命令允許用戶自行指定被存儲整數的類型,並且不會使用對象去包裹這些整數,因此當我們想要存儲長度比long類型短的整數,並且希望盡可能地減少對象包裹帶來的內存消耗時,就可以考慮使用位圖來存儲整數。
-
因為位圖是使用字符串實現的,所以字符串命令也可以用於處理位圖命令。但是在使用字符串命令操作位圖時,用戶必須先把命令返回的字符串值轉換成二進制值,然后再進行后續處理。