redis-bitmap在簽到和統計狀態項目中的妙用

1、使用redis-bitmap背景和優點

1.項目背景

  • 最近作的需求中,有個每日簽到功能,須要用戶每日簽到,而且統計累計簽到了多少天等這樣的信息。
  • 在思考項目方案的時候,最簡單的就是mysql,記錄下用戶天天簽到的數據,每一個用戶天天簽到的時候都insert一條數據,利用mysql統計起來也超級方便,然而;
  • 思考一個問題,一個用戶一年的簽到數據將是365條,若是用戶數大的話,將是365*用戶數這麼多條記錄,這些記錄僅僅是一個狀態數據,一個簽到狀態實際上用「1」或者「0」這樣的數字就能徹底表示出來,那麼有沒有更好更節省存儲空間的方式呢?
  • 實際上,遇到這種只須要存儲 「是」或者「否」 狀態的數據的時候,能夠嘗試是否可使用bitmap這種數據結構來存儲數據。

2.什麼是bitmap

  • bitmap能夠想象成一個圍棋盤這種點位陣,每一個格子表明一個bit位,只能存儲1或者0兩種數據。
  • 所以,bitmap使用場景實際上就是隻能存儲「狀態」類型數據,好比咱們將在項目中用到的簽到數據,由於簽到狀態只有兩種,一種已簽到能夠用「1」表示,一種未簽到能夠用「0」表示。

3.bitmap優點

  • bitmap最大的優點就是能夠存儲海量數據而且快速檢索到具體的數據。
  • 咱們再回到以前說的簽到功能的mysql方案,一個用戶一天的簽到數據包含uid、sign_status、created_at,最起碼三個int類型字段,一個int類型是4個字節,三個int就是12個字節,也就是一個用戶一天的數據最低佔12個字節,千萬用戶一年的數據是1000w x 365 x 12=438000w字節=42773M,請記住這個42773M,是mysql存儲佔用的空間,大約41G。
  • 那麼咱們來計算一下用bitmap存儲千萬用戶一年的數據佔據多少空間。假如咱們用戶uid大部分是連續的,那麼天天全部的用戶簽到數據都存在一個key,好比uid=1000的用戶簽到,就將這個key的第1000位設置爲1,這樣實際上全部用戶一年的數據只有365條,一條數據保存1000w個bit,因爲一個字節8個bit,就是125w字節,那麼千萬用戶一年的數據是125w x 365=45625w字節=445M。
  • 以上,咱們知道,使用mysql保存一年千萬用戶最低42773M空間,而使用bitmap保存一年千萬用戶最低445M,bitmap空間只佔了mysql的1%,這個差異真的是天差地別。
  • 另外bitmap查詢數據的時候,redis提供了相關命令能夠直接統計一個key中有多少個1,也能夠直接獲取到具體那個位bit的值。
  • 其中,在一臺2010MacBook Pro上,offset爲2^32-1(分配512MB)須要~300ms,offset爲2^30-1(分配128MB)須要~80ms,offset爲2^28-1(分配32MB)須要~30ms,offset爲2^26-1(分配8MB)須要8ms。因此查詢某個位的值的時候速度也是至關快,redis中一個string類型的key限制不能超過512M。

2、redis-bitmap相關命令介紹

  1. Setbitphp

     語法:setbit key offset value
    
      描述:
    
        對key所儲存的字符串值,設置或清除指定偏移量上的位(bit)。
    
        位的設置或清除取決於 `value` 參數,能夠是 `0` 也能夠是 `1` 。
    
        當 `key` 不存在時,自動生成一個新的字符串值。
    
        字符串會進行伸展(grown)以確保它能夠將 `value` 保存在指定的偏移量上。當字符串值進行伸展時,空白位置以 `0` 填充。
    
     注意:
    
        `offset` 參數必須大於或等於 `0` ,小於 2^32 (bit 映射被限制在 512 MB 以內)。
    
        由於 Redis 字符串的大小被限制在 512 兆(megabytes)之內, 因此用戶可以使用的最大偏移量爲 2^29-1(536870911) , 若是你須要使用比這更大的空間, 請使用多個 `key。`
    
        當生成一個很長的字符串時, Redis 須要分配內存空間,  該操做有時候可能會形成服務器阻塞(block)。 在2010年出產的Macbook Pro上, 設置偏移量爲 536870911(512MB  內存分配)將耗費約 300 毫秒, 設置偏移量爲 134217728(128MB 內存分配)將耗費約 80 毫秒, 設置偏移量  33554432(32MB 內存分配)將耗費約 30 毫秒, 設置偏移量爲 8388608(8MB 內存分配)將耗費約 8 毫秒。
  2. getbitmysql

    語法:getbit key offset
    
       描述:
    
        對 key 所儲存的字符串值,獲取指定偏移量上的位(bit)。
    
        當 offset 比字符串值的長度大,或者 key 不存在時,返回 0
  3. bitcountredis

    語法:bitcount key [start] [end]
    
      返回值:被設置爲 1 的位的數量
    
      描述:
    
        計算給定字符串中,被設置爲 1 的比特位的數量
    
        通常狀況下,給定的整個字符串都會被進行計數,經過指定額外的 start 或 end 參數,可讓計數只在特定的字節上進行。注意不是bit位,是字節。
          例如:假如key1的value是00001100 11001000 11110000
          <1> bitcount key1 0 0 
              這個是獲取key1中第0個字節組中bit爲1的count,也就是00001100 中查詢,返回2
          <2> bitcount key1 0 1 
              這個是獲取key1中第0-1個字節組中bit爲1的count,也就是00001100 11001000中查詢,返回5  
          <3> bitcount key1 1 2 
              這個是獲取key1中第1-2個字節組中bit爲1的count,也就是11001000 11110000中查詢,返回7   
    
        start 和 end 參數的設置和 GETRANGE key start end 命令相似,均可以使用負數值: 好比 -1表示最後一個bit, -2 表示倒數第二個bit,以此類推。
    
        不存在的 key 被當成是空字符串來處理,所以對一個不存在的 key 進行 BITCOUNT 操做,結果爲 0 。
  4. bitpossql

    語法: bitpos key bit [start] [end] 
         
      返回值:返回字符串裏面第一個被設置爲1或者0的bit位。
    
      描述:
    
        返回一個位置,把字符串當作一個從左到右的字節數組,第一個符合條件的在位置0,其次在位置8,等等。
    
        默認狀況下整個字符串都會被檢索一次,只有在指定start和end參數(指定start和end位是可行的),該範圍被解釋爲一個字節的範圍,而不是一系列的位。因此start=0 而且 end=2是指前三個字節範圍內查找。
  5. bitop數組

    語法:bitop operation destkey key [key ...]
    
        operation 能夠是 AND 、 OR 、 NOT 、 XOR 這四種操做中的任意一種:
    
              BITOP AND destkey key [key ...] ,對一個或多個 key 求邏輯並,並將結果保存到 destkey 。
              BITOP OR destkey key [key ...] ,對一個或多個 key 求邏輯或,並將結果保存到 destkey 。
              BITOP XOR destkey key [key ...] ,對一個或多個 key 求邏輯異或,並將結果保存到 destkey 。
              BITOP NOT destkey key ,對給定 key 求邏輯非,並將結果保存到 destkey 。
    
        除了 NOT 操做以外,其餘操做均可以接受一個或多個 key 做爲輸入。    
    
      返回值:保存到 destkey 的字符串的長度,和輸入 key 中最長的字符串長度相等
    
      描述:
    
        對一個或多個保存二進制位的字符串 key 進行位元操做,並將結果保存到 destkey 上。
    
      注意:處理不一樣長度的字符串
    
        當 BITOP 處理不一樣長度的字符串時,較短的那個字符串所缺乏的部分會被看做 0 。
    
        空的 key 也被看做是包含 0 的字符串序列。
  6. bitfield服務器

    語法:bitfield key [GET type offset] [SET type offset value] [INCRBY type offset increment] [OVERFLOW WRAP|SAT|FAIL]
    
         
    
      描述:
    
        該命令將 Redis 字符串視爲一個位數組,而且可以處理具備不一樣位寬和任意非(必要)對齊偏移量的特定整數字段。
        實際上,使用此命令能夠將位偏移量爲1234的帶符號5位整數設置爲特定值,從偏移量4567中檢索31位無符號整數。相似地,該命令處理指定整數的遞增和遞減,提供保證和良好指定的溢出和下溢行爲,用戶能夠配置。
    
      注意:詳情能夠查看文章:https://cloud.tencent.com/developer/section/1374165

3、bitmap應用場景

1.用戶每日簽到

  • 用戶每日簽到,可使用redis-bitmap,關於key的保存方式,有不一樣方案,一種是一個key保存一天內全部用戶的簽到狀態,這種方式注意,若是大部分簽到的用戶uid並不連續,那麼整個key看到將會有大量的00000000,只有零星的1,因此這種方式適用於連續uid數據保存;
  • 另外一種key存值方式,就是以key=uid_year_month保存一個用戶一個月的簽到數據,這種適合用戶量少的方式。

2.統計用戶活躍用戶

  • 使用時間做爲key,用戶uid做爲offset,活躍過就設置爲1
  • 遇到一樣的問題,就是uid不連續怎麼辦?
  • 這種方式能夠考慮分片保存:例如uid對1000取模獲得的數都是小於1000的,uid小於1000的做爲分片1,uid在1000~2000做爲分片2,那麼能夠設置key=分片id,具體的位就是uid對1000取模獲得的值,這樣保存的時候只是須要先肯定當前uid在哪一個分片便可。

4、思考及其餘注意的點

  1. 具體的bitmap保存方式,要儘可能考慮本身業務狀況,經過計算具體哪一種方式保存更節省空間來肯定。
  2. redis中一個key限制不能大於512M,bit位個數儘可能小於2^29-1(536870911),大約5億,別最後超限制了。
相關文章
相關標籤/搜索