深入淺析Redis中的位圖(bitmap)

本篇文章帶大家了解一下redis中的位圖(bitmap),希望對(duì)大家有所幫助!

深入淺析Redis中的位圖(bitmap)

redis 的位圖(bitmap)是由多個(gè)二進(jìn)制位組成的數(shù)組,數(shù)組中的每個(gè)二進(jìn)制位都有與之對(duì)應(yīng)的偏移量(從 0 開(kāi)始),通過(guò)這些偏移量可以對(duì)位圖中指定的一個(gè)或多個(gè)二進(jìn)制位進(jìn)行操作。【相關(guān)推薦:Redis視頻教程

實(shí)際上,位圖并不是 Redis 提供的一種新的數(shù)據(jù)類型,它是字符串類型的擴(kuò)展。所以位圖的命令可以直接使用在字符串類型的鍵上,位圖命令操作的鍵也可以被字符串類型命令操作。

比如,現(xiàn)有一個(gè)字符串鍵 foo:

redis>?set?foo?bar

1 個(gè)字節(jié)由 8 個(gè)二進(jìn)制位組成,所以 foo 鍵的二進(jìn)制形式就是:

深入淺析Redis中的位圖(bitmap)

SETBIT

通過(guò) SETBIT 命令,可以為位圖指定偏移量上的二進(jìn)制位設(shè)置值,offset 必須大于等于 0,value 只能是 0 或 1。此命令的時(shí)間復(fù)雜度是 O(1)。

SETBIT key offset value

SETBIT 命令在對(duì)二進(jìn)制位進(jìn)行設(shè)置之后,將返回二進(jìn)制位被設(shè)置之前的舊值作為結(jié)果。

假設(shè)現(xiàn)在想把 bar 變?yōu)?aar,只需如下兩步操作:

redis>?setbit?foo?6?0 (integer)?1 redis>?setbit?foo?7?1 (integer)?0 redis>?get?foo"aar"

當(dāng)執(zhí)行 SETBIT 命令嘗試對(duì)一個(gè)位圖進(jìn)行設(shè)置的時(shí)候,如果位圖不存在,或者位圖當(dāng)前的大小無(wú)法滿足,Redis 將對(duì)被設(shè)置的位圖進(jìn)行擴(kuò)展,并將所有未被設(shè)置的二進(jìn)制位的值初始化為 0。比如:

redis>?setbit?far?10?1

由于 far 并不存在,所以 Redis 會(huì)將 0~9 的二進(jìn)制位設(shè)置為 0,因?yàn)?Redis 對(duì)位圖的擴(kuò)展操作是以字節(jié)為單位進(jìn)行的,所以實(shí)際上 far 一共有 16 個(gè)二進(jìn)制位,并不是 10 個(gè),且 11~15 的二進(jìn)制位也是 0。

基于這種情況,當(dāng)指定的二進(jìn)制位偏移量過(guò)大時(shí),Redis 需要一次性分配完所有內(nèi)存,這可能會(huì)造成 Redis 服務(wù)器阻塞。比如存儲(chǔ)用戶的性別,1 代表男性,0 代表女性,使用 ID 作為二進(jìn)制偏移量,如果 ID 從 10000000001 開(kāi)始的,就需要將用戶 ID 減去 10000000000 再進(jìn)行存儲(chǔ),否則會(huì)造成內(nèi)存浪費(fèi)。

GETBIT

使用 GETBIT 命令可以獲取位圖指定偏移量上的二進(jìn)制位的值。此命令的時(shí)間復(fù)雜度是 O(1)。

GETBIT key offset

如果輸入的偏移量超過(guò)了位圖目前擁有的最大偏移量,將返回 0 作為結(jié)果。

BITCOUNT

通過(guò) BITCOUNT 命令可以統(tǒng)計(jì)位圖中值為 1 的二進(jìn)制位數(shù)量。此命令的時(shí)間復(fù)雜度是 O(n)。

BITCOUNT key [start end]

在默認(rèn)情況下,BITCOUNT 命令對(duì)位圖包含的所有字節(jié)中的二進(jìn)制位進(jìn)行統(tǒng)計(jì),也可以通過(guò)可選的 start 參數(shù)和 end 參數(shù),讓 BITCOUNT 只對(duì)指定字節(jié)范圍內(nèi)的二進(jìn)制位進(jìn)行統(tǒng)計(jì)(不是二進(jìn)制偏移量)。比如,希望統(tǒng)計(jì) ar 兩個(gè)字節(jié)中值為 1 的二進(jìn)制數(shù)量:

redis>?bitcount?foo?1?2 (integer)?7

start 和 end 參數(shù)也支持使用負(fù)數(shù)索引,下方的用法與上方的等效:

redis>?bitcount?foo?-2?-1 (integer)?7

BITPOS

通過(guò)執(zhí)行 BITPOS 命令,在位圖中查找第一個(gè)被設(shè)置為指定值的二進(jìn)制位,并返回這個(gè)二進(jìn)制位的偏移量。

BITPOS key value [start end]

BITPOS 也接受可選的 start 參數(shù)和 end 參數(shù),讓 BITPOS 命令只在指定字節(jié)范圍內(nèi)的二進(jìn)制位中進(jìn)行查找。

redis>?get?foo"aar"redis>?bitpos?foo?1 (integer)?1 redis>?bitpos?foo?0 (integer)?0 redis>?bitpos?foo?0?1?2 (integer)?8 redis>?bitpos?foo?1?1?2 (integer)?9 redis>?bitpos?foo?1?-1?-1 (integer)?17

針對(duì)邊界的處理:

  • 當(dāng)嘗試對(duì)一個(gè)不存在的位圖或者一個(gè)所有位都被設(shè)置成了 0 的位圖中查找值為 1 的二進(jìn)制位時(shí),BITPOS 命令將返回 -1 作為結(jié)果。
  • 如果在一個(gè)所有位都被設(shè)置成 1 的位圖中查找值為 0 的二進(jìn)制位,那么 BITPOS 命令將返回位圖最大偏移量加上 1 作為結(jié)果

BITOP

通過(guò) BITOP 命令,對(duì)一個(gè)或多個(gè)位圖執(zhí)行指定的二進(jìn)制位運(yùn)算,并將運(yùn)算結(jié)果存儲(chǔ)到指定的鍵中。

BITOP?operation destkey key [key …]

operation 參數(shù)的值可以是 AND、OR、XOR、NOT 中的任意一個(gè),這 4 個(gè)值分別對(duì)應(yīng)邏輯并、邏輯或、邏輯異或和邏輯非 4 種運(yùn)算,其中 AND、OR、XOR 這 3 種運(yùn)算允許用戶使用任意數(shù)量的位圖作為輸入,而 NOT 運(yùn)算只允許使用一個(gè)位圖作為輸入。BITOP 命令在將計(jì)算結(jié)果存儲(chǔ)到指定鍵中之后,會(huì)返回被存儲(chǔ)位圖的字節(jié)長(zhǎng)度。

當(dāng) BITOP 命令在對(duì)兩個(gè)長(zhǎng)度不同的位圖執(zhí)行運(yùn)算時(shí),會(huì)將長(zhǎng)度較短的那個(gè)位圖中不存在的二進(jìn)制位的值看作 0。

redis>?set?foo1?bar OK redis>?set?foo2?aar OK redis>?bitop?or?res?foo1?foo2 (integer)?3 redis>?get?res"car"

深入淺析Redis中的位圖(bitmap)

注意:BITOP 可能是一個(gè)緩慢的命令,它的時(shí)間復(fù)雜度是 O(N),在處理長(zhǎng)字符串時(shí)應(yīng)注意一下效率問(wèn)題。

應(yīng)用場(chǎng)景

用戶行為記錄器

用用戶 ID 作為偏移量,若用戶做了某種行為則通過(guò) SETBIT 將二進(jìn)制位設(shè)置為 1,通過(guò) GETBIT 判斷用戶是否做了某種行為,通過(guò) BITCOUNT 可以知道有多少用戶執(zhí)行了行為。

用戶上線統(tǒng)計(jì)

可以使用 SETBIT 和?BITCOUNT?來(lái)實(shí)現(xiàn),以用戶 ID 作為 key ,假設(shè)今天是上線統(tǒng)計(jì)功能開(kāi)放的第一天,ID 為 1 的用戶上線后就通過(guò) SETBIT 1 0 1。當(dāng)要計(jì)算此用戶的總共以來(lái)的上線次數(shù)時(shí),使用?BITCOUNT?命令就可以得出的結(jié)果。

使用這種方式存儲(chǔ)數(shù)據(jù),即使 10 年后,1個(gè)用戶就只占用幾百字節(jié)的內(nèi)存,它的處理速度依然很快。如果 bitmap 數(shù)據(jù)比較大,建議將 bitmap 拆分成多個(gè)小的 bitmap 分別進(jìn)行處理。

更多編程相關(guān)知識(shí),請(qǐng)?jiān)L問(wèn):Redis視頻教程!!

? 版權(quán)聲明
THE END
喜歡就支持一下吧
點(diǎn)贊11 分享