Redis 布隆過濾器

2021-09-11 17:23:04 字數 3664 閱讀 1849

想知道某乙個值是不是已經在 hyperloglog 結構裡面了,它就無能為力了,它只提供了 pfadd 和 pfcount 方法,沒有提供 pfcontains 這種方法。

講個使用場景,比如我們在使用新聞客戶端看新聞時,它會給我們不停地推薦新的內容,它每次推薦時要去重,去掉那些已經看過的內容。問題來了,新聞客戶端推薦系統如何實現推送去重的?

布隆過濾器可以理解為乙個不怎麼精確的 set 結構,當你使用它的 contains 方法判斷某個物件是否存在時,它可能會誤判。但是布隆過濾器也不是特別不精確,只要引數設定的合理,它的精確度可以控制的相對足夠精確,只會有小小的誤判概率。

redis 官方提供的布隆過濾器到了 redis 4.0 提供了外掛程式功能之後才正式登場。布隆過濾器作為乙個外掛程式載入到 redis server 中,給 redis 提供了強大的布隆去重功能。

下面我們來體驗一下 redis 4.0 的布隆過濾器,為了省去繁瑣安裝過程,我們直接用 docker 吧。

> docker pull redislabs/rebloom  # 拉取映象

> docker run -p6379:6379 redislabs/rebloom # 執行容器

> redis-cli # 連線容器中的 redis 服務

如果上面三條指令執行沒有問題,下面就可以體驗布隆過濾器了。

布隆過濾器有二個基本指令,bf.add新增元素,bf.exists查詢元素是否存在,它的用法和 set 集合的 sadd 和 sismember 差不多。注意bf.add只能一次新增乙個元素,如果想要一次新增多個,就需要用到bf.madd指令。同樣如果需要一次查詢多個元素是否存在,就需要用到bf.mexists指令。

127.0.0.1:6379> bf.add codehole user1

(integer) 1

127.0.0.1:6379> bf.add codehole user2

(integer) 1

127.0.0.1:6379> bf.add codehole user3

(integer) 1

127.0.0.1:6379> bf.exists codehole user1

(integer) 1

127.0.0.1:6379> bf.exists codehole user2

(integer) 1

127.0.0.1:6379> bf.exists codehole user3

(integer) 1

127.0.0.1:6379> bf.exists codehole user4

(integer) 0

127.0.0.1:6379> bf.madd codehole user4 user5 user6

1) (integer) 1

2) (integer) 1

3) (integer) 1

127.0.0.1:6379> bf.mexists codehole user4 user5 user6 user7

1) (integer) 1

2) (integer) 1

3) (integer) 1

4) (integer) 0

public class bloomtest 

}client.close();

}}

誤判率大約 1% 多點。你也許會問這個誤判率還是有點高啊,有沒有辦法降低一點?答案是有的。

我們上面使用的布隆過濾器只是預設引數的布隆過濾器,它在我們第一次 add 的時候自動建立。redis 其實還提供了自定義引數的布隆過濾器,需要我們在 add 之前使用bf.reserve指令顯式建立。如果對應的 key 已經存在,bf.reserve會報錯。bf.reserve有三個引數,分別是 key,error_rateinitial_size。錯誤率越低,需要的空間越大。initial_size引數表示預計放入的元素數量,當實際數量超出這個數值時,誤判率會上公升。

所以需要提前設定乙個較大的數值避免超出導致誤判率公升高。如果不使用 bf.reserve,預設的error_rate是 0.01,預設的initial_size是 100。

接下來我們使用 bf.reserve 改造一下上面的指令碼:

public class bloomtest 

chars = builder.tostring();

} private string randomstring(int n)

return builder.tostring();

} private listrandomusers(int n)

return users;

} public static void main(string args)

int falses = 0;

for (string user : userstest)

}system.out.printf("%d %d\n", falses, userstest.size());

client.close();

}}

執行一下,等待約 1 分鐘,輸出如下:

total users 100000

all trained

6 50000

我們看到了誤判率大約 0.012%,比預計的 0.1% 低很多,不過布隆的概率是有誤差的,只要不比預計誤判率高太多,都是正常現象。

布隆過濾器的initial_size估計的過大,會浪費儲存空間,估計的過小,就會影響準確率,使用者在使用之前一定要盡可能地精確估計好元素數量,還需要加上一定的冗餘空間以避免實際元素可能會意外高出估計值很多。

布隆過濾器的error_rate越小,需要的儲存空間就越大,對於不需要過於精確的場合,error_rate設定稍大一點也無傷大雅。比如在新聞去重上而言,誤判率高一點只會讓小部分文章不能讓合適的人看到,文章的整體閱讀量不會因為這點誤判率就帶來巨大的改變。

學會了布隆過濾器的使用,下面有必要把原理解釋一下,不然讀者還會繼續蒙在鼓裡

每個布隆過濾器對應到 redis 的資料結構裡面就是乙個大型的位陣列和幾個不一樣的無偏 hash 函式。所謂無偏就是能夠把元素的 hash 值算得比較均勻。

向布隆過濾器中新增 key 時,會使用多個 hash 函式對 key 進行 hash 算得乙個整數索引值然後對位陣列長度進行取模運算得到乙個位置,每個 hash 函式都會算得乙個不同的位置。再把位陣列的這幾個位置都置為 1 就完成了 add 操作。

向布隆過濾器詢問 key 是否存在時,跟 add 一樣,也會把 hash 的幾個位置都算出來,看看位陣列中這幾個位置是否都為 1,只要有乙個位為 0,那麼說明布隆過濾器中這個 key 不存在。如果都是 1,這並不能說明這個 key 就一定存在,只是極有可能存在,因為這些位被置為 1 可能是因為其它的 key 存在所致。如果這個位陣列比較稀疏,判斷正確的概率就會很大,如果這個位陣列比較擁擠,判斷正確的概率就會降低。

Redis 布隆過濾器

實現原理 對的資料肯定是對的,錯的資料不能保證是對的。解釋如下 之前新增到容器中的資料,再次進行判斷是不是在這裡面,因為來過,槽位肯定我為1,所以判斷是不會出現問題的。容器中不存在,判斷是不是在容器中,可能其他的資料通過hash函式執行後把槽位占用了,所以存在一定的誤差。不能保證一定沒問題。hash...

redis 布隆過濾器

為了應對redis快取穿透有三個大致方案 做好引數校驗,快取無效key,以及布隆過濾器。通過布隆過濾器我們可以非常方便地判斷乙個給定資料是否存在於海量資料中。我們需要的就是判斷 key 是否合法 具體是這樣做的 把所有可能存在的請求的值都存放在布隆過濾器中,當使用者請求過來,先判斷使用者發來的請求的...

redis 布隆過濾器

簡述 這個過濾器可以檢索乙個元素是否在乙個集合中 優點是查詢效率很高,缺點是有一定概率出現誤判 可以理解為 利用高效的資料結構和演算法快速判斷出你這個 key 是否在資料庫中存在,不存在就 return 存在就去查詢 db資料庫 重新整理 kv 再 return。可以通過 來看下效果 先導入依賴 d...