bitmap原理和redis bitmap應用


bitmap原理

bitmap是什么?在計算機中一個字節(byte)=8位(bit),這里的bit就是位,數據的最小表示單位,map一般是表示地圖或者映射。

簡單回顧一下二進制的一些知識:

1byte=8bit

1個bit有二種狀態:0或1

所以1個byte可以表示00000000->11111111,也就是十進制0-255

其中十進制和二進制的對應關系如下:

0 ---------> 0000 0000
 1 ---------> 0000 0001
 2 ---------> 0000 0010
 3 ---------> 0000 0011
 4 ---------> 0000 0100
 5 ---------> 0000 0101
 6 ---------> 0000 0110
 7 ---------> 0000 0111
 8 ---------> 0000 1000
 9 ---------> 0000 1001
10 ---------> 0000 1010
11 ---------> 0000 1011
12 ---------> 0000 1100
13 ---------> 0000 1101
14 ---------> 0000 1110
15 ---------> 0000 1111
.......................
.......................
255...........1111 1111

在大部分變成語言里,int類型一般的都是占4個byte,也就是32位,甭管你這個數字是1或者是21億你都得棧32位,所以如果你現在有10億數字需要存在內存里面,需要分配多少內存呢:

1000000000 * 4 / 1024 / 1024 = 3800MB,大概需要3800MB內存,這里計算出的數值只適合C,在PHP里面,一個整形變量占用的實際空間遠遠大於4byte,是好幾倍。

為了解決這個問題,bitmap采用一種映射機制,舉個例子,假如有1、3、7、2、5這5個數字需要存放,正常情況下需要5*4bye=20byte,但是bitmap只需要1byte,它是如何做到的?

假設下面是1byte,首先將所有位置為0:00000000

從第一個0開始數數,把對應數字的位置置位1,比如說第一個1那就是第2個位置置位1,第二個三就是把第4個位置置位1,以此類推

1 => 0 1 0 0 0 0 0 0
3 => 0 0 0 1 0 0 0 0
7 => 0 0 0 0 0 0 0 1
2 => 0 0 1 0 0 0 0 0
5 => 0 0 0 0 0 1 0 0

累加起來最終的串就是:0 1 1 1 0 1 0 1

其實最終的數字和二進制沒什么關系,純粹是數數,這個串就代表最大到7的數字,然后我們就開始數數,從0開始:

比如第1個位置是1,那就記個1
比如第2個位置是1,那就記個2
比如第3個位置是1,那就記個3
比如第5個位置是1,那就記個5
比如第7個位置是1,那就記個7

結果就是1 2 3 5 7,不僅僅排序,而且還排重了,如果按照這種轉換機制,1個int類型,32位的話,可以表示0-31之間的數字

如果要表示最大一萬的數,就需要1萬個位的串,但是編程語言並沒有這樣的數據類型,但是可以用數組去模擬。舉個例子,一個整形是32位,也就說我們大概需要314個數組元素來表示這個串:

  • 數組第1個元素 00 - 31
  • 數組第2個元素 32 - 63
  • 數組第3個元素 64 - 95
  • 數組第4個元素 96 - 127
  • ……

提到這個算法的好處,最大的好處就是節省內存,節省了好幾十倍,適合處理大量數據,除了快速排序,還可以快速去重,快速查詢是否存在,還有一個好聽的應用Bloom Filter(布隆過濾器)

 

 redis bitmap應用

setBit

說明:給一個指定的key的值的第offset位賦值位value

參數:key offset value:bool or int (1 or 0)

返回值:long:0或1

 getBit

說明:返回一個指定key的二進制信息

參數:key offset

返回值:long

bitCount

說明:返回一個指定key中位的值為1的個數(是以byte為單位的bit)

參數:key start offset

返回值:long

bitOp

說明:對不同的二進制存儲數據進行位運算(AND、OR、NOT、XOR)

參數:operation destkey key [key …]

 返回值:long

 

bitmap優勢:

  1. 基於最小的單位bit進行存儲,所以非常省空間
  2. 設置時候時間復雜度O(1),讀取時候時間復雜度O(n),操作時非常快的
  3. 二進制數據的存儲,進行相關計算的時候非常快
  4. 方便擴容

 bitmap限制:

redis中bit映射並限制在512MB之內,所以最大時2^32位。建議每個key的位數都控制下,因為讀取時候時間復雜度O(n),越大的串讀的時間花銷越大。

 

bitmap使用場景:

1.視頻屬性的無限延伸:

需求分析:一個擁有億級數據量的短視頻app,視頻存在各種屬性(是否加鎖、是否特效等等),需要做各種標記。

可能想到的解決方案:

  1. 存儲在mysql中,肯定不行,一個是隨着業務增長屬性一直增加,並且存在有時間限制的屬性,直接對數據庫進行加減字段是非常不合理的做法。即使是存在一個字段中用json等壓縮技術存儲也存在讀效率的問題,並且對於大幾億的數據來說,廢棄的字段回收起來非常麻煩。
  2. 直接記錄在redis中,根據業務屬性+uid為key來存儲。讀寫效率角度沒毛病,但是存儲的角度來說key的數據量都大於value了,太耗費空間了。即使是用json等壓縮技術來存儲。也存在問題,解壓需要時間,並且大幾億的數據回收也是難題。

設計方案:

使用redis的bitmap進行存儲。
key由屬性id+視頻分片id組成。value按照視頻id對分片范圍取模來決定偏移量offset。10億視頻一個屬性約120m還是挺划算的。

偽代碼:

 

 

2.用戶在線狀態

 需求分析:需要對子項目提供一個接口,來提供某用戶是否在線?

設計方案:使用bitmap是一個節約空間效率又高的一種方法,只需要一個key,然后用戶id為偏移量offset,如果在線就設置為1,不在線就設置為0,3億用戶只需要36MB的空間。

偽代碼:

 

 

 3.統計活躍用戶

需求分析:需要計算活躍用戶的數據情況。

設計方案:使用時間作為緩存的key,然后用戶id為offset,如果當日活躍過就設置為1。之后通過bitOp進行二進制計算算出在某段時間內用戶的活躍情況。

偽代碼:

 

 

4.用戶簽到

需求分析:用戶需要進行簽到,對於簽到的數據需要進行分析與相應的運運營策略。

設計方案:使用redis的bitmap,由於是長尾的記錄,所以key主要由uid組成,設定一個初始時間,往后沒加一天即對應value中的offset的位置。

偽代碼:

 

 

 

 

 

 

 原文出處:

https://blog.csdn.net/u011957758/article/details/74783347

https://zhuanlan.zhihu.com/p/67920410


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM