您當前的位置:首頁 > 曲藝

記一篇REDIS布隆過濾器的使用

作者:由 Free 發表于 曲藝時間:2019-11-03

開場:

如何判斷一個大集合中是否含有某個元素?

背景:

為了最大化提升廣告轉化效果,業務方決定對接巨量引擎,廣點通以及快手RTA服務。並針對自身情況決定是否出廣告(新使用者出,舊使用者不出)。

記一篇REDIS布隆過濾器的使用

大致示意圖

要求: 1。 QPS至少要能撐住

30W。

2。 介面響應不能超過

60ms

面臨的問題:

1。高併發——> 負載均衡這塊交由中臺完成(部署到k8s, 由40個pod分攤掉流量)。

2。低延遲 ——> 響應要快,計算不能太耗時。

調研:

對於低延遲,快速判斷請求側使用者資訊是不是老使用者,需要對舊使用者池進行檢索。儲存方面自然想到redis。

記一篇REDIS布隆過濾器的使用

檢索示意圖

資料量級: 目前舊使用者池約有

6億

條資料, 考慮到裝置標識多樣性(IDFA, IMEI, Android_id。。), 以及大小寫問題,這個資料量可以估算為

15億

左右(需要多預估一些空間)。

挑選資料結構: String。 每一個裝置都是一個key, 請求方只需要看這個key在不在即可

實現:

key: key字首+md5(device)。

例: app:old_users:202cb962ac59075b964b07152d234b70

值: 1

寫命令: SET app:old_users:202cb962ac59075b964b07152d234b70 1

讀命令: EXISTS app:old_users:202cb962ac59075b964b07152d234b70

空間預估:

size = key.length * 16億 = 46B * 16 * 10^8 = 68GB

這種方案成本較高,且會隨著使用者增加線性提升,直接pass。需要找到一種查詢快,且佔用空間小的方案。

抽象:

判斷一個裝置是否在舊使用者池,如果把使用者池看成集合,把裝置看做元素,那麼就可以回到開場的問題——如何判斷一個大集合中是否含有某個元素?

布隆過濾器

似乎是最佳的解決方案。

使用布隆過濾器的好處:

省空間

: 不需要儲存完整的元素,只需要對元素進行hash然後將bit向量表中的某個位設為1即可(先不考慮碰撞問題)

查詢快

: 只需要對查詢的元素進行hash然後看bit向量表中對應的位是否為1。

缺點:

1。因為碰撞,會有一定的誤報率( 不在集合的一定不在, 在的不一定在 )。這個可以透過使用多個hash函式減少誤報,但無法完全消除。

2。不支援刪除操作(還是因為碰撞,會出現誤刪的問題)。

有關布隆過濾器的詳解可以參考

結合REDIS

redis原生並不自帶布隆過濾器, 需要專門下載並自行編譯和載入,使用方法見

這裡用到布隆過濾器兩個API

1。讀方: BF。EXISTS KEY element

2。寫方: BF。ADD KEY element

兩個API即可完成布隆過濾器元素的增加和查詢操作(注: 這裡的KEY就是布隆過濾器)。

拆KEY:

阿里雲的redis單機只能經受10WQPS,再往上就不行了;所以需要選擇叢集版redis, 用叢集來承擔讀方的查詢壓力。那麼需要將key打散並分攤到不同的節點上。

拆key的規則: 取md5(device)頭四位拼接,這樣頭四位相同的裝置會落到相同的key裡(布隆過濾器)

例: deviceArray = [

“202cb962ac59075b964b07152d234b70”,

“202cb35dac59075b964b07152d234b95”,

“202cb35dac09875b964b07152d234b88”,

。。。。

對應的寫命令:

BF。ADD app:old_users:202c 202cb962ac59075b964b07152d234b70

BF。ADD app:old_users:202c 202cb35dac59075b964b07152d234b95

BF。ADD app:old_users:202c 202cb35dac09875b964b07152d234b88

。。。。

空間預估: key的數量最多為 16^4(十六進位制) 。

size = key.length 16^4 = 18B * 65536 = 1.125MB

這樣key所佔的空間可以忽略。

上線效果:

記一篇REDIS布隆過濾器的使用

線上介面QPS: 峰值26W, 谷底4W,平響9ms。

redis使用情況:key數: 13w+(因為一些原因要增加其它的key), 佔用記憶體:3。19GB(布隆過濾器本身以及bit向量都需要佔用空間)。

總結:

專案做完後,有一些值得思考的地方,整理一下

1。提前預估(要為專案的以後做打算)

QPS預估: 最開始是按最小峰值預估的(10W左右),沒有考慮業務發展的情況(後期接近30W)。如果不是領導提醒,自己就傻乎乎的按10W的量去做了,那redis的選型就會有問題(主從版redis只能支援10w,後來改為叢集版),以後要估高一些,若資源使用率不高,再降配也不晚。

2。布隆過濾器使用(技術方案要儘可能通用)

開發時使用redis布隆過濾器的時候,用的是本地載入官方元件並啟動redis,並沒有考慮到阿里雲是否支援;結果預設是不支援的。。。 好在後來跟對方協商,對方可以提供定製化版的redis,可以支援布隆過濾器的API。 這個事後還是有點後怕的, 如果它不支援呢? 如果要遷移到別的雲呢?這個其實是留下了隱患的。 技術方案要有備選(用bitmap+偏移量實現, 但是hash演算法要找輪子或自己實現)

3。抽象能力

這個非常重要,最終的技術方案並不是自己想到的,而是經過領導指點的。在特定的業務場景下,用合適的資料結構解決業務上的問題,為什麼別人能抽象到具體的方案,而自己沒想到? 歸根結底還是對資料結構和算法理解的不夠透徹, 這塊是需要深耕的地方。

標簽: Key  布隆  過濾器  Redis  APP