Redis中位圖和HyperLogLog的應用

      在我們日常開發中,會遇到一些布爾類型數據存儲的需求,說的直白一些,就是是與不是、做與沒做的一些需求,像用戶的簽到並記錄這些簽到,和辦公系統裏面打卡是一樣的,下面兩張圖就是我的支付寶與我的移動的簽到應用。

 

 

當接到這樣的需求時,第一時間我想到的就是使用Redis來應對這樣的需求,用戶一年的簽到記錄, 簽了是 1,沒簽是 0,要記錄 365 天。如果使用普通的 key/value,每個用戶要記錄 365 個,當用戶上億的時候,需要的存儲空間是驚人的。而這時Redis的位圖數據結構就派上用場了,這樣每天的簽到記錄只佔據一個位, 365 天就是 365 個位,一個字節是8位,也就是一個用戶簽到一年,差不多46 個字節就可以完全容納下,這就大大節約了存儲空間。

位圖不是特殊的數據結構,它的內容其實就是普通的字符串,也就是 byte 數組。我們可以使用普通的 get/set 直接獲取和設置整個位圖的內容,也可以使用位圖操作 getbit/setbit 等將 byte 數組看成「位數組」來處理。

操作:設置(setbit)和獲取(getbit):

操作:統計(bitcount)和查找(bitpos):

以上就是Redis提供給我們位圖的數據結構,當然還是其他操作,這裏就不再贅述,下面來講一下另外一個需求,那就是統計網站的UV數據,當接到這樣一個需求時,相信大部分程序員都會漏出自信的微笑,我用一個Redis的計數器就可以實現了,其實不然,如果統計 PV 那非常好辦,給每個網頁一個獨立的 Redis 計數器就可以了,這個計數器 的 key 後綴加上當天的日期。這樣來一個請求,incrby 一次,最終就可以統計出所有的 PV 數據。但是 UV 不一樣,它要去重,同一個用戶一天之內的多次訪問請求只能計數一次。這就 要求每一個網頁請求都需要帶上用戶的 ID,無論是登陸用戶還是未登陸用戶都需要一個唯一 ID 來標識。可能你會想到另外一個簡單的方案,那就是爲每一個頁面一個獨立的 set 集合來存儲所有當天訪問過此頁面的用戶 ID。當一個請求過來時,我們使用 sadd 將用戶 ID 塞進去就可 以了。通過 scard 可以取出這個集合的大小,這個數字就是這個頁面的 UV 數據。不錯,這也是一個實現方案,但這個方案也有缺點,那就是數據量比較大時,如果你的頁面訪問量非常大,比如一個爆款頁面幾千萬的 UV,你需要一個很大 的 set 集合來統計,這就非常浪費空間。

鋪墊了那麼久,其實就是爲了引出這個解決方案的主角,那就是HyperLogLog,Redis 提供了 HyperLogLog 數據結構就是用來解決 這種統計問題的。HyperLogLog 提供不精確的去重計數方案,雖然不精確但是也不是非常不精確,標準誤差是不到1%,這樣的精確度已經可以滿足上面的 UV 統計需求了。

操作:增加(pfadd)和統計(pfcount):

大家看了會說,這部挺準確的,添加了3個,統計出來的就是3個,量大了就不行了,下面我們就用python腳步跑一個,驗證一下,打開我們心愛的ipython:

誤差是有一點的,但是可以滿足相應的需求了,加入產品對統計精度要求特別的苛刻,那麼我們就要損失存儲空間和性能來實現它的需求了,兵來將擋,水來土掩,利用好Redis提供的強大功能,來開發應對不同的需求,不要只是會添加、獲取和刪除緩存這樣簡單的功能,要物盡其用!

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章