關(guān)于Redis中bitmap的原理和使用詳解
一、原理
先聲明一下:Redis 有5種數(shù)據(jù)類型,而 BitMap 在 Redis 中并不是一個(gè)新的數(shù)據(jù)類型,其底層是 Redis 實(shí)現(xiàn)。
通常情況下,我們?cè)?redis 中存儲(chǔ)一個(gè)字符串,如:“big”,它的位圖如下:
0.001kb = 1b = 8bit
所以,字符串“big”占3個(gè)字符,也就是24個(gè)bit位。
Redis 從 2.2.0 版本開始新增了 setbit,getbit,bitcount 等幾個(gè) bitmap 相關(guān)命令。雖然是新命令,但是并沒(méi)有新增新的數(shù)據(jù)類型,因?yàn)?setbit 等命令只不過(guò)是在 set 上的擴(kuò)展。
利用上述命令,Redis 可以操作二進(jìn)制的位,可以取/改每一個(gè)位對(duì)應(yīng)的值,簡(jiǎn)單寫幾個(gè):
127.0.0.1:6379 > set hello big "OK" 127.0.0.1:6379 > getbit hello 0 "0" 127.0.0.1:6379 > getbit hello 1 "1" 127.0.0.1:6379 > setbit hello 7 1 "0" 127.0.0.1:6379 > get hello "cig"
通過(guò)上面的例子,我們可以發(fā)現(xiàn):
- getbit,setbit 可以對(duì)字符串進(jìn)行位操作,可以獲取/修改某位上的值;
- 字符串的位修改以后,字符串本身也發(fā)生了根本變化,big -> cig。
BitMap 原本的含義是用一個(gè) bit 位來(lái)進(jìn)行0或者1的設(shè)置,映射某個(gè)元素的狀態(tài)。
由于一個(gè)比特位只能表示 0 和 1 兩種狀態(tài),也就是說(shuō)一個(gè) bit 能存儲(chǔ)的最多信息量是 2,所以 BitMap 能映射的狀態(tài)有限,但是使用比特位的優(yōu)勢(shì)是能大量的節(jié)省內(nèi)存空間。
二、BitMap 相關(guān)命令
在 Redis 中,Bitmap 是一串連續(xù)的2進(jìn)制數(shù)字(0或1),所以,可以把 Bitmaps 想象成一個(gè)以比特位為單位的數(shù)組,數(shù)組的每一位所在的位置為偏移(offset),數(shù)組的下標(biāo)在 Bitmaps 中叫做偏移量,在 bitmap 上可執(zhí)行AND,OR,XOR以及其它位操作。
# 設(shè)置值,其中value只能是 0 和 1 setbit key offset value # 獲取值 getbit key offset # 獲取指定范圍內(nèi)值為 1 的個(gè)數(shù) # start 和 end 以字節(jié)為單位 bitcount key start end # BitMap間的運(yùn)算 # operations 位移操作符,枚舉值 AND 與運(yùn)算 & OR 或運(yùn)算 | XOR 異或 ^ NOT 取反 ~ # result 計(jì)算的結(jié)果,會(huì)存儲(chǔ)在該key中 # key1 … keyn 參與運(yùn)算的key,可以有多個(gè),空格分割,not運(yùn)算只能一個(gè)key # 當(dāng) BITOP 處理不同長(zhǎng)度的字符串時(shí),較短的那個(gè)字符串所缺少的部分會(huì)被看作 0。返回值是保存到 destkey 的字符串的長(zhǎng)度(以字節(jié)byte為單位),和輸入 key 中最長(zhǎng)的字符串長(zhǎng)度相等。 bitop [operations] [result] [key1] [keyn…] # 返回指定key中第一次出現(xiàn)指定value(0/1)的位置 bitpos [key] [value]
三、BitMap 空間計(jì)算
因?yàn)?BitMap 中的 bit 位 是 字符串的映射,字符串在 value 中的存儲(chǔ)是有上限的,所以 BitMap 的valu額存儲(chǔ)空間可以用相同的方式計(jì)算。
Redis 中字符串的最大長(zhǎng)度是 512M,所以 BitMap 的 offset (偏移量)最大值為:
512 * 1024 * 1024 * 8 = 2^32
四、使用場(chǎng)景
1. 用戶簽到
很多網(wǎng)站都提供了簽到功能,并且需要展示最近一個(gè)月的簽到情況,這種情況可以使用 BitMap 來(lái)實(shí)現(xiàn)。 根據(jù)日期 offset = (今天是一年中的第幾天) % (今年的天數(shù)),key = 年份:用戶id。
如果需要將用戶的詳細(xì)簽到信息入庫(kù)的話,可以考慮使用一個(gè)一步線程來(lái)完成。
# 2021年第一天,用戶Id = userId 的用戶簽到 setbit 2021:userId 1 1
2. 統(tǒng)計(jì)活躍用戶(用戶登陸情況)
使用日期作為 key,然后用戶 id 為 offset,如果當(dāng)日活躍過(guò)就設(shè)置為1。具體怎么樣才算活躍這個(gè)標(biāo)準(zhǔn)大家可以自己指定。
假如:
- 20220101 活躍用戶情況是: [1,0,1,1,0]
- 20220102 活躍用戶情況是 :[ 1,1,0,1,0 ]
統(tǒng)計(jì)連續(xù)兩天活躍的用戶總數(shù):
bitop and dest1 20220101 20220102 # dest1 中值為1的offset,就是連續(xù)兩天活躍用戶的ID bitcount dest1
統(tǒng)計(jì)20220101 ~ 20220102 活躍過(guò)的用戶總數(shù):
bitop or dest2 20220101 20220102 # dest2 中值為1的offset,就是兩天都活躍的用戶的ID bitcount dest2
3. 統(tǒng)計(jì)用戶在線狀態(tài)
如果需要提供一個(gè)查詢當(dāng)前用戶是否在線的接口,也可以考慮使用 BitMap ,即節(jié)約空間效率又高,只需要一個(gè) key,然后用戶 id 為 offset,如果在線就設(shè)置為 1,不在線就設(shè)置為 0。
# userId 登錄,設(shè)置狀態(tài)為1 setbit key userId 1 # 獲取 userId 的狀態(tài):1 - 在線;0 - 不在線 getbit key userId
4. 實(shí)現(xiàn)布隆過(guò)濾器
布隆過(guò)濾器解決緩存穿透。
五、總結(jié)
- bigmap 基于最小的單位bit進(jìn)行存儲(chǔ),最大優(yōu)勢(shì)是非常省空間;
- 設(shè)置時(shí)候時(shí)間復(fù)雜度O(1)、讀取時(shí)候時(shí)間復(fù)雜度O(n),操作是非常快的;
- 二進(jìn)制數(shù)據(jù)的存儲(chǔ),進(jìn)行相關(guān)計(jì)算的時(shí)候非???,也能方便擴(kuò)容;
- 不要給一個(gè)很短的 bigmap 設(shè)置很長(zhǎng)位的偏移量的值,這樣有可能堵塞。
到此這篇關(guān)于關(guān)于Redis中bitmap的原理和使用詳解的文章就介紹到這了,更多相關(guān)Redis的bitmap原理和使用內(nèi)容請(qǐng)搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!
相關(guān)文章
Redis實(shí)現(xiàn)Session共享與單點(diǎn)登錄
本文主要介紹了Redis實(shí)現(xiàn)Session共享與單點(diǎn)登錄,文中通過(guò)示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面隨著小編來(lái)一起學(xué)習(xí)學(xué)習(xí)吧2022-07-07使用RediSearch實(shí)現(xiàn)在Redis中全文檢索
RediSearch?是?Redis?的一個(gè)插件,它為?Redis?數(shù)據(jù)庫(kù)添加了全文搜索和查詢功能,使開發(fā)人員能夠在?Redis?中高效地執(zhí)行全文檢索操作,下面我們就來(lái)看看是具體如何使用的吧2023-08-08redis監(jiān)聽key過(guò)期事件的詳細(xì)步驟
本文主要介紹了redis監(jiān)聽key過(guò)期事件的詳細(xì)步驟,文中通過(guò)示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面隨著小編來(lái)一起學(xué)習(xí)學(xué)習(xí)吧2022-08-08如何利用Redis?List實(shí)現(xiàn)Java數(shù)據(jù)庫(kù)分頁(yè)快速查詢
這篇文章主要給大家介紹了關(guān)于如何利用Redis?List實(shí)現(xiàn)Java數(shù)據(jù)庫(kù)分頁(yè)快速查詢的相關(guān)資料,Redis是一個(gè)高效的內(nèi)存數(shù)據(jù)庫(kù),它支持包括String、List、Set、SortedSet和Hash等數(shù)據(jù)類型的存儲(chǔ),需要的朋友可以參考下2024-02-02Redis 如何批量設(shè)置過(guò)期時(shí)間(PIPLINE的使用)
有時(shí)候我們并不希望redis的key一直存在。例如緩存,驗(yàn)證碼等數(shù)據(jù),我們希望它們能在一定時(shí)間內(nèi)自動(dòng)的被銷毀。本文就詳細(xì)的介紹一下Redis 如何批量設(shè)置過(guò)期時(shí)間,感興趣的可以了解一下2021-11-11