天天看點

Redis開發與運維. 3.5 Bitmaps

<b>3.5 bitmaps</b>

<b>3.5.1 資料結構模型</b>

現代計算機用二進制(位)作為資訊的基礎機關,1個位元組等于8位,例如“big”字元串是由3個位元組組成,但實際在計算機存儲時将其用二進制表示,“big”分别對應的ascii碼分别是98、105、103,對應的二進制分别是01100010、01101001和01100111,如圖3-9所示。

圖3-9 字元串“big”用二進制表示

許多開發語言都提供了操作位的功能,合理地使用位能夠有效地提高記憶體使用率和開發效率。redis提供了bitmaps這個“資料結構”可以實作對位的操作。把資料結構加上引号主要因為:

bitmaps本身不是一種資料結構,實際上它就是字元串(如圖3-10所示),但是它可以對字元串的位進行操作。

bitmaps單獨提供了一套指令,是以在redis中使用bitmaps和使用字元串的方法不太相同。可以把bitmaps想象成一個以位為機關的數組,數組的每個單元隻能存儲0和1,數組的下标在bitmaps中叫做偏移量。

圖3-10 字元串"big"用二進制表示

<b>3.5.2 指令</b>

本節将每個獨立使用者是否通路過網站存放在bitmaps中,将通路的使用者記做1,沒有通路的使用者記做0,用偏移量作為使用者的id。

1.?設定值

setbit key offset value

設定鍵的第offset個位的值(從0算起),假設現在有20個使用者,userid=0,5,11,15,19的使用者對網站進行了通路,那麼目前bitmaps初始化結果如圖3-11所示。

圖3-11 setbit使用

具體操作過程如下,unique:users:2016-04-05代表2016-04-05這天的獨立通路使用者的bitmaps:

127.0.0.1:6379&gt; setbit

unique:users:2016-04-05 0 1

(integer) 0

unique:users:2016-04-05 5 1

unique:users:2016-04-05 11 1

unique:users:2016-04-05 15 1

unique:users:2016-04-05 19 1

如果此時有一個userid=50的使用者通路了網站,那麼bitmaps的結構變成了圖3-12,第20位~49位都是0。

圖3-12 userid=50使用者通路

很多應用的使用者id以一個指定數字(例如10000)開頭,直接将使用者id和bitmaps的偏移量對應勢必會造成一定的浪費,通常的做法是每次做setbit操作時将使用者id減去這個指定數字。在第一次初始化bitmaps時,假如偏移量非常大,那麼整個初始化過程執行會比較慢,可能會造成redis的阻塞。

2.?擷取值

gitbit key offset

擷取鍵的第offset位的值(從0開始算),下面操作擷取id=8的使用者是否在2016-04-05這天通路過,傳回0說明沒有通路過:

127.0.0.1:6379&gt; getbit

unique:users:2016-04-05 8

由于offset=1000000根本就不存在,是以傳回結果也是0:

unique:users:2016-04-05 1000000

3.?擷取bitmaps指定範圍值為1的個數

bitcount [start]?[end]

下面操作計算2016-04-05這天的獨立通路使用者數量:

127.0.0.1:6379&gt; bitcount

unique:users:2016-04-05

(integer) 5

[start]和[end]代表起始和結束位元組數,下面操作計算使用者id在第1個位元組到第3個位元組之間的獨立通路使用者數,對應的使用者id是11,15,19。

unique:users:2016-04-05 1 3

(integer) 3

4.?bitmaps間的運算

bitop op destkey key?[key....]

bitop是一個複合操作,它可以做多個bitmaps的and(交集)、or(并集)、not(非)、xor(異或)操作并将結果儲存在destkey中。假設2016-04-04通路網站的userid=1,2,5,9,如圖3-13所示。

圖3-13 2016-04-04通路網站的使用者bitmaps

下面操作計算出2016-04-04和2016-04-03兩天都通路過網站的使用者數量,如圖3-14所示。

127.0.0.1:6379&gt; bitop and

unique:users:and:2016-04-04_03 unique: users:2016-04-03

unique:users:2016-04-03

(integer) 2

unique:users:and:2016-04-04_03

如果想算出2016-04-04和2016-04-03任意一天都通路過網站的使用者數量(例如月活躍就是類似這種),可以使用or求并集,具體指令如下:

127.0.0.1:6379&gt; bitop or

unique:users:or:2016-04-04_03 unique:

users:2016-04-03 unique:users:2016-04-03

unique:users:or:2016-04-04_03

(integer) 6

圖3-14 利用bitop and指令計算兩天都通路網站的使用者

5.?計算bitmaps中第一個值為targetbit的偏移量

bitpos key targetbit [start] [end]

下面操作計算2016-04-04目前通路網站的最小使用者id:

127.0.0.1:6379&gt; bitpos

unique:users:2016-04-04 1

(integer) 1

除此之外,bitops有兩個選項[start]和[end],分别代表起始位元組和結束位元組,例如計算第0個位元組到第1個位元組之間,第一個值為0的偏移量,從圖3-13可以得知結果是id=0的使用者。

unique:users:2016-04-04 0 0 1

<b>3.5.3 bitmaps分析</b>

假設網站有1億使用者,每天獨立通路的使用者有5千萬,如果每天用集合類型和bitmaps分别存儲活躍使用者可以得到表3-3。

表3-3 set和bitmaps存儲一天活躍使用者的對比

資料類型         每個使用者id占用空間    需要存儲的使用者量         全部記憶體量

集合類型         64位        50?000?000     64位×50?000?000 = 400mb

bitmaps   1位 100?000?000  1位×100?000?000

= 12.5mb

很明顯,這種情況下使用bitmaps能節省很多的記憶體空間,尤其是随着時間推移節省的記憶體還是非常可觀的,見表3-4。

表3-4 set和bitmaps存儲獨立使用者空間對比

         一天         一個月     一年

set   400m        12g  144g

bitmaps   12.5m      375m        4.5g

但bitmaps并不是萬金油,假如該網站每天的獨立通路使用者很少,例如隻有10萬(大量的僵屍使用者),那麼兩者的對比如表3-5所示,很顯然,這時候使用bitmaps就不太合适了,因為基本上大部分位都是0。

表3-5 set和bitmaps存儲一天活躍使用者的對比(獨立使用者比較少)

資料類型         每個userid占用空間     需要存儲的使用者量         全部記憶體量

集合類型         64位        100?000  64位 × 100?000 = 800kb

bitmaps   1位 100?000?000  1位 × 100?000?000 = 12.5mb

繼續閱讀