淺談Redis位圖(Bitmap)及Redis二進制中的問題
Redis位圖(Bitmap)及二進制的問題
SETBIT key offset value
對 key 所儲存的字符串值,設(shè)置或清除指定偏移量上的位(bit)。位的設(shè)置或清除取決于 value 參數(shù),可以是 0 也可以是 1 。當 key 不存在時,自動生成一個新的字符串值。字符串會進行伸展(grown)以確保它可以將 value 保存在指定的偏移量上。當字符串值進行伸展時,空白位置以 0 填充。offset 參數(shù)必須大于或等于 0 ,小于 2^32 (bit 映射被限制在 512 MB 之內(nèi))。返回值是指定偏移量原來存儲的位
對使用大的 offset 的 SETBIT 操作來說,內(nèi)存分配可能造成 Redis 服務(wù)器被阻塞。具體參考 SETRANGE 命令,warning(警告)部分。
127.0.0.1:6379> setbit bit 3 1 (integer) 0 127.0.0.1:6379> getbit bit 0 (integer) 0 127.0.0.1:6379> getbit bit 1 (integer) 0 127.0.0.1:6379> getbit bit 2 (integer) 0 127.0.0.1:6379> getbit bit 3 (integer) 1127.0.0.1:6379> setbit bit 3 0(integer) 1
GETBIT key offset
返回key對應(yīng)的string在offset處的bit值,當offset超出了字符串長度的時候,這個字符串就被假定為由0比特填充的連續(xù)空間。當key不存在的時候,它就認為是一個空字符串,所以offset總是超出范圍,然后value也被認為是由0比特填充的連續(xù)空間。到內(nèi)存分配。
127.0.0.1:6379> getbit yhq 5 (integer) 0 127.0.0.1:6379> getbit bit 10 (integer) 0 127.0.0.1:6379> getbit bit 3 (integer) 1
BITCOUNT key [start] [end]
計算給定字符串中,被設(shè)置為 1 的比特位的數(shù)量。一般情況下,給定的整個字符串都會被進行計數(shù),通過指定額外的 start 或 end 參數(shù),可以讓計數(shù)只在特定的位上進行*start和end的單元是字節(jié)而不是bit*。start 和 end 參數(shù)的設(shè)置和 GETRANGE 命令類似,都可以使用負數(shù)值:比如 -1 表示最后一個位,而 -2 表示倒數(shù)第二個位,以此類推。不存在的 key 被當成是空字符串來處理,因此對一個不存在的 key 進行 BITCOUNT 操作,結(jié)果為 0 。
127.0.0.1:6379> set mykey foobar OK 127.0.0.1:6379> bitcount youkey (integer) 0 127.0.0.1:6379> bitcount mykey (integer) 26 127.0.0.1:6379> bitcount mykey 0 0 # "f" 0110 0110 (integer) 4 127.0.0.1:6379> bitcount mykey 1 1 # "o" 0110 1111 (integer) 6
BITOP operation destkey key [key ...]
對一個或多個保存二進制位的字符串 key 進行位元操作,并將結(jié)果保存到 destkey 上。
operation 可以是 AND 、 OR 、 NOT 、 XOR 這四種操作中的任意一種:
- BITOP AND destkey key [key ...] ,對一個或多個 key 求邏輯與,并將結(jié)果保存到 destkey 。
- BITOP OR destkey key [key ...] ,對一個或多個 key 求邏輯或,并將結(jié)果保存到 destkey 。
- BITOP XOR destkey key [key ...] ,對一個或多個 key 求邏輯異或,并將結(jié)果保存到 destkey 。
- BITOP NOT destkey key ,對給定 key 求邏輯非,并將結(jié)果保存到 destkey 。
除了 NOT 操作之外,其他操作都可以接受一個或多個 key 作為輸入。
處理不同長度的字符串,當 BITOP 處理不同長度的字符串時,較短的那個字符串所缺少的部分會被看作 0 。空的 key 也被看作是包含 0 的字符串序列
127.0.0.1:6379> setbit bit1 0 1 (integer) 0 127.0.0.1:6379> setbit bit1 3 1 (integer) 0 127.0.0.1:6379> setbit bit2 0 1 (integer) 0 127.0.0.1:6379> setbit bit2 1 1 (integer) 0 127.0.0.1:6379> setbit bit2 3 1 (integer) 0 127.0.0.1:6379> bitop and andbit bit1 bit2 (integer) 1 127.0.0.1:6379> getbit andbit 0 (integer) 1 127.0.0.1:6379> getbit andbit 1 (integer) 0 127.0.0.1:6379> getbit andbit 2 (integer) 0 127.0.0.1:6379> getbit andbit 3 (integer) 1
BITPOS key bit [start] [end]
返回字符串里面第一個被設(shè)置為1或者0的bit位。如果我們在空字符串或者0字節(jié)的字符串里面查找bit為1的內(nèi)容,那么結(jié)果將返回-1。
如果我們在字符串里面查找bit為0而且字符串只包含1的值時,將返回字符串最右邊的第一個空位。如果有一個字符串是三個字節(jié)的值為0xff的字符串,那么命令BITPOS key 0將會返回24,因為0-23位都是1。基本上,我們可以把字符串看成右邊有無數(shù)個0。然而,如果你用指定start和end范圍進行查找指定值時,如果該范圍內(nèi)沒有對應(yīng)值,結(jié)果將返回-1。
127.0.0.1:6379> getbit num 0 (integer) 0 127.0.0.1:6379> getbit num 1 (integer) 0 127.0.0.1:6379> getbit num 2 (integer) 1 127.0.0.1:6379> getbit num 3 (integer) 1 127.0.0.1:6379> getbit num 4 (integer) 0 127.0.0.1:6379> getbit num 5 (integer) 0 127.0.0.1:6379> getbit num 6 (integer) 1 127.0.0.1:6379> getbit num 7 (integer) 0 127.0.0.1:6379> getbit num 8 (integer) 0 127.0.0.1:6379> bitpos num 1 (integer) 2 127.0.0.1:6379> bitpos yhqqhh 1 (integer) -1 127.0.0.1:6379> bitpos yhqqhh 0 (integer) 0
Redis二進制中的問題1 : 數(shù)字全部是char類型表示
127.0.0.1:6379> set num 2 OK 127.0.0.1:6379> bitcount num (integer) 3
Redis中,數(shù)字類型其實是以ASCII形式展現(xiàn)的,即 2=>50,正常2的(一個字節(jié)8個二進制位)表示為 00000010,bitcount為1。而Redis數(shù)字是字符的"2",所以 "2"[50] 的表示為 00110010,bitcount為3。
Redis中二進制從左到右(正常從右到左)
"2"的二進制為 00110010
127.0.0.1:6379> getbit num 0 (integer) 0 127.0.0.1:6379> getbit num 1 (integer) 0 127.0.0.1:6379> getbit num 2 (integer) 1 127.0.0.1:6379> getbit num 3 (integer) 1 127.0.0.1:6379> getbit num 4 (integer) 0 127.0.0.1:6379> getbit num 5 (integer) 0 127.0.0.1:6379> getbit num 6 (integer) 1 127.0.0.1:6379> getbit num 7 (integer) 0
redis高級數(shù)據(jù)結(jié)構(gòu)---bitmap
場景引入
我們在正常開發(fā)環(huán)境中,有時候需要將bool型數(shù)據(jù)進行存取,比如用戶一年里面簽到了多少次,簽到了設(shè)置1,沒簽到設(shè)置0,要記錄365天,如果使用普通的key/value形式存儲,每個用戶就需要占據(jù)365鍵值對,當用戶量上億的時候,需要驚人的存儲空間。更何況是一年的。 為了解決這種問題,redis提出了bitmap的數(shù)據(jù)結(jié)構(gòu),這樣每天用戶簽到只需要占據(jù)一個位,365天就是365位,46個字節(jié),一個稍微長一點的字符串就可以完全容納下一個用戶一年的簽到記錄,大量的節(jié)省存儲空間。位圖的最小單位是比特(bit),每個bit的取值只能是0或1。
實現(xiàn)原理
位圖不是特殊的數(shù)據(jù)結(jié)構(gòu),他的內(nèi)容實際就是普通的字符串,也就是byte數(shù)組,我們可以使用普通的get/set直接獲取和設(shè)置整個位圖的內(nèi)容,也可以使用位圖操作getbit/setbit等將byte數(shù)組看成位數(shù)組來處理。
基本用法
redis的位數(shù)組是自動擴展的,如果設(shè)置了某個偏移位置超出了現(xiàn)有的內(nèi)容范圍,就會自動將位數(shù)組進行零擴充。
舉例:
“h”的ASCII碼值是:01101000
"e"的ASCII碼值是: 01100101
"l"的ASCII碼值是:0110 1100
"o"的ASCII碼值是:0110 1111
將“he” 連起來是:0110100001100101
即1,2,4,9,10,13,15位為1
以上的示范可以稱之為“零存整取”,即使用單個位操作設(shè)置位值,使用單個位操作獲取具體位值。
還有另一種操作稱之為“整存零取”,即使用字符串操作批量設(shè)置值,使用單個位操作獲取具體位值。
以上介紹了setbit,getbit的操作,redis還提供了位圖的統(tǒng)計和查找指令:bitcount,bitpos
bitcount同來統(tǒng)計指定位值范圍內(nèi)1的個數(shù)。
bitpos用來查找指定范圍內(nèi)出現(xiàn)的第一個0或者1。
127.0.0.1:6379> set w hello OK 127.0.0.1:6379> bitcount w //統(tǒng)計所有的1的個數(shù) (integer) 21 127.0.0.1:6379> bitcount w 0 0 //統(tǒng)計第一個字符中1的個數(shù) (integer) 3 127.0.0.1:6379> bitcount w 0 1 //統(tǒng)計前兩個字符中1的個數(shù) (integer) 7 127.0.0.1:6379> bitpos w 0 //第一個0位 (integer) 0 127.0.0.1:6379> bitpos w 1 //第一個1位 (integer) 1 127.0.0.1:6379> bitpos w 1 1 1 // 從第二個字符算起,第一個1位 (integer) 9 127.0.0.1:6379> bitpos w 1 2 2 // 從第三個字符算起,第一個1位 (integer) 17 127.0.0.1:6379>
接下來介紹魔術(shù)指令 bitfield:
主要解決setbit/getbit只能操作單個位的弊端。redis 3.2+新增功能。
bitfield有三個子指令:get、set、incrby,他們都可以對指定位片段進行讀寫,但是最多只能處理64個連續(xù)的位,如果超過64位,就得使用多個子指令,bitfield可以一次執(zhí)行多個子指令。
127.0.0.1:6379> set w hello OK 127.0.0.1:6379> bitfield w get u4 0 //從第一個位開始取4個位,結(jié)果是無符號數(shù)(u) 1) (integer) 6 127.0.0.1:6379> bitfield w get u3 2 //從第三個位開始取3個位,結(jié)果是無符號數(shù) 1) (integer) 5 127.0.0.1:6379> bitfield w get i4 0 //從第一個位開始取4個位,結(jié)果是有符號數(shù) (i) 1) (integer) 6 127.0.0.1:6379> bitfield w get i3 2 //從第三個位開始取3個位,結(jié)果是有符號數(shù) 1) (integer) -3 127.0.0.1:6379>
所謂有符號數(shù)是指獲取的位數(shù)組中第一個位是符號位,剩下的才是值,如果第一個位是1,那就是負數(shù)。
無符號數(shù)表示非負數(shù),沒有符號位,獲取的位數(shù)全部是是值。
有符號數(shù)最多可以獲取64位,無符號數(shù)只能獲取63位。如果超出限制,redis會報參數(shù)錯誤。
接下來演示一個多指令:
127.0.0.1:6379> bitfield w get u4 0 get u3 2 get i4 0 get i3 2 1) (integer) 6 2) (integer) 5 3) (integer) 6 4) (integer) -3 127.0.0.1:6379>
接下來使用set子指令將第二個字符e,改成a,a的ASCII值是97
127.0.0.1:6379> bitfield w set u8 8 97 1) (integer) 101 127.0.0.1:6379> get w "hallo" 127.0.0.1:6379>
接下來介紹第三個子指令incrby,他用來對指定范圍的位進行自增操作,既然是自增操作,就會存在溢出的情況,如果增加了正數(shù),會出現(xiàn)向上溢出,如果是增加了負數(shù),就會出現(xiàn)向下溢出。redis的默認處理方式是折返操作,如果出現(xiàn)了溢出,就將溢出的符號位丟掉。如果是8位無符號數(shù)255,加1后就會溢出,會全部變?yōu)?.如果是8位有符號數(shù)127,加1后就會溢出變成-128。
127.0.0.1:6379> set w hello OK 127.0.0.1:6379> bitfield w incrby u4 2 1 //從第三個位開始,對接下來的4位無符號數(shù)進行自增+1 1) (integer) 11 127.0.0.1:6379> bitfield w incrby u4 2 1 1) (integer) 12 127.0.0.1:6379> bitfield w incrby u4 2 1 1) (integer) 13 127.0.0.1:6379> bitfield w incrby u4 2 1 1) (integer) 14 127.0.0.1:6379> bitfield w incrby u4 2 1 1) (integer) 15 127.0.0.1:6379> bitfield w incrby u4 2 1 //出現(xiàn)了溢出折返現(xiàn)象 1) (integer) 0 127.0.0.1:6379>
bitfield指令提供了溢出策略子指令overflow,用戶可以選擇溢出行為,默認是折返(wrap),還可以選擇失?。╢ail)------報錯不執(zhí)行,以及飽和截斷(sat)-----超過了范圍就停留在最大值或者最小值。overflow指令只影響接下來的第一條指令,這條指令執(zhí)行完后溢出策略會變成默認值折返。
飽和截斷:
127.0.0.1:6379> set w hello OK 127.0.0.1:6379> bitfield w overflow sat incrby u4 2 1 1) (integer) 11 127.0.0.1:6379> bitfield w overflow sat incrby u4 2 1 1) (integer) 12 127.0.0.1:6379> bitfield w overflow sat incrby u4 2 1 1) (integer) 13 127.0.0.1:6379> bitfield w overflow sat incrby u4 2 1 1) (integer) 14 127.0.0.1:6379> bitfield w overflow sat incrby u4 2 1 1) (integer) 15 127.0.0.1:6379> bitfield w overflow sat incrby u4 2 1 //出現(xiàn)飽和截斷,保持最大值 1) (integer) 15 127.0.0.1:6379>
失敗不執(zhí)行:
127.0.0.1:6379> set w hello OK 127.0.0.1:6379> bitfield w overflow fail incrby u4 2 1 1) (integer) 11 127.0.0.1:6379> bitfield w overflow fail incrby u4 2 1 1) (integer) 12 127.0.0.1:6379> bitfield w overflow fail incrby u4 2 1 1) (integer) 13 127.0.0.1:6379> bitfield w overflow fail incrby u4 2 1 1) (integer) 14 127.0.0.1:6379> bitfield w overflow fail incrby u4 2 1 1) (integer) 15 127.0.0.1:6379> bitfield w overflow fail incrby u4 2 1 //不執(zhí)行 1) (nil) 127.0.0.1:6379>
以上為個人經(jīng)驗,希望能給大家一個參考,也希望大家多多支持腳本之家。
相關(guān)文章
Springboot/Springcloud項目集成redis進行存取的過程解析
大家都知道Redis支持五種數(shù)據(jù)類型:string(字符串),hash(哈希),list(列表),set(集合),zset(sorted set:有序集合),本文重點給大家介紹Springboot/Springcloud項目集成redis進行存取的過程,需要的朋友參考下吧2021-12-12Redis基本數(shù)據(jù)類型Zset有序集合常用操作
這篇文章主要為大家介紹了redis基本數(shù)據(jù)類型Zset有序集合常用操作,有需要的朋友可以借鑒參考下,希望能夠有所幫助,祝大家多多進步,早日升職加薪2022-05-05Redis的數(shù)據(jù)存儲及String類型的實現(xiàn)
這篇文章主要介紹了Redis的數(shù)據(jù)存儲及String類型的實現(xiàn),redis作為k-v數(shù)據(jù)存儲,因查找和操作的時間復(fù)雜度都是O(1)和豐富的數(shù)據(jù)類型及數(shù)據(jù)結(jié)構(gòu)的優(yōu)化,了解了這些數(shù)據(jù)類型和結(jié)構(gòu)更有利于我們平時對于redis的使用,需要的朋友可以參考下2022-10-10使用redis實現(xiàn)延遲通知功能(Redis過期鍵通知)
這篇文章主要介紹了使用redis實現(xiàn)延遲通知功能(Redis過期鍵通知)的相關(guān)知識,本文通過實例代碼圖文相結(jié)合給大家介紹的非常詳細,對大家的學(xué)習(xí)或工作具有一定的參考借鑒價值,需要的朋友參考下吧2021-09-09