06_redis_布隆過濾器

推送推薦內(nèi)容去重淆珊,使用bloom filter
相當(dāng)于一個(gè)不怎么精確的set結(jié)構(gòu)拇颅,當(dāng)使用contain方法判斷一個(gè)對象時(shí)候存在的時(shí)候會(huì)誤判捐晶,但是只要參數(shù)合理,它的精確程度還是很高的拴驮。
當(dāng)布隆過濾器判斷不存在的時(shí)候是真的不存在,判斷存在的時(shí)候可能不存在柴信。
Redis4.0提供插件功能套啤,布隆過濾器需要加載一個(gè) 插件到Redis Server中

基本使用

bf.add 添加元素,bf.exists 查詢元素是否存在随常。
bf.add 只能一次添加一個(gè)元素潜沦,如果想要一次添加多個(gè),就需要用到 bf.madd 指令绪氛。同樣如果需要一次查詢多個(gè)元素是否存在唆鸡,就需要用到 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

Java 客戶端 Jedis-2.x 沒有提供指令擴(kuò)展機(jī)制枣察,所以你無法直接使用 Jedis 來訪問Redis Module 提供的 bf.xxx 指令争占。
可以使用 lettuce,它是另一個(gè)Redis 的客戶端序目,相比 Jedis 而言臂痕,它很早就支持了指令擴(kuò)展。

public class BloomTest {
    public static void main(String[] args) {
        Client client = new Client();
        client.delete("codehole");
        for (int i = 0; i < 100000; i++) {
            client.add("codehole", "user" + i);
            boolean ret = client.exists("codehole", "user" + i);
            if (!ret) {
                System.out.println(i);
                break;
            }
        }
        client.close();
    }
}

使用 bf.exists 去查找沒見過的元素猿涨,看看它是不是以為自己見過了握童。

public class BloomTest {
    public static void main(String[] args) {
        Client client = new Client();
        client.delete("codehole");
        for (int i = 0; i < 100000; i++) {
            client.add("codehole", "user" + i);
            boolean ret = client.exists("codehole", "user" + (i + 1));
            if (ret) {
                System.out.println(i);
                break;
            }
        }
        client.close();
    }
}

運(yùn)行后,我們看到了輸出是 214叛赚,也就是到第 214 的時(shí)候舆瘪,它出現(xiàn)了誤判。

測量誤判率

public class BloomTest {
import com.rabbitmq.http.client.Client;

import java.util.ArrayList;
import java.util.List;
import java.util.concurrent.ThreadLocalRandom;

class BloomTest {
    private String chars;

    {
        StringBuilder builder = new StringBuilder();
        for (int i = 0; i < 26; i++) {
            builder.append((char) ('a' + i));
        }
        chars = builder.toString();
    }

    private String randomString(int n) {
        StringBuilder builder = new StringBuilder();
        for (int i = 0; i < n; i++) {
            int idx = ThreadLocalRandom.current().nextInt(chars.length());
            builder.append(chars.charAt(idx));
        }
        return builder.toString();
    }

    private List<String> randomUsers(int n) {
        List<String> users = new ArrayList<>();
        for (int i = 0; i < 100000; i++) {
            users.add(randomString(64));
        }
        return users;
    }

    public static void main(String[] args) {
        BloomTest bloomer = new BloomTest();
        List<String> users = bloomer.randomUsers(100000);
        List<String> usersTrain = users.subList(0, users.size() / 2);
        List<String> usersTest = users.subList(users.size() / 2, users.size());
        Client client = new Client();
        client.delete("codehole");
        for (String user : usersTrain) {
            client.add("codehole", user);
        }
        int falses = 0;
        for (String user : usersTest) {
            boolean ret = client.exists("codehole", user);
            if (ret) {
                falses++;
            }
        }
        System.out.printf("%d %d\n", falses, usersTest.size());
        client.close();
    
    }
}

誤判率大約 1% 多點(diǎn)红伦。

public class BloomTest {
    private String chars;

    {
        StringBuilder builder = new StringBuilder();
        for (int i = 0; i < 26; i++) {
            builder.append((char) ('a' + i));
        }
        chars = builder.toString();
    }

    private String randomString(int n) {
        StringBuilder builder = new StringBuilder();
        for (int i = 0; i < n; i++) {
            int idx = ThreadLocalRandom.current().nextInt(chars.length());
            builder.append(chars.charAt(idx));
        }
        return builder.toString();
    }

    private List<String> randomUsers(int n) {
        List<String> users = new ArrayList<>();
        for (int i = 0; i < 100000; i++) {
            users.add(randomString(64));
        }
        return users;
    }

    public static void main(String[] args) {
        BloomTest bloomer = new BloomTest();
        List<String> users = bloomer.randomUsers(100000);
        List<String> usersTrain = users.subList(0, users.size() / 2);
        List<String> usersTest = users.subList(users.size() / 2, users.size());
        Client client = new Client();
        client.delete("codehole");
        // 對應(yīng) bf.reserve 指令
        client.createFilter("codehole", 50000, 0.001);
        for (String user : usersTrain) {
            client.add("codehole", user);
        }
        int falses = 0;
        for (String user : usersTest) {
            boolean ret = client.exists("codehole", user);
            if (ret) {
                falses++;
            }
        }
        System.out.printf("%d %d\n", falses, usersTest.size());
        client.close();
    }
}

誤判率大約 0.012%英古,比預(yù)計(jì)的 0.1% 低很多,不過布隆的概率是有誤差的昙读,只要不比預(yù)計(jì)誤判率高太多召调,都是正常現(xiàn)象

布隆過濾器的 initial_size 估計(jì)的過大,會(huì)浪費(fèi)存儲(chǔ)空間唠叛,估計(jì)的過小只嚣,就會(huì)影響準(zhǔn)確率,用戶在使用之前一定要盡可能地精確估計(jì)好元素?cái)?shù)量艺沼,還需要加上一定的冗余空間以避免實(shí)際元素可能會(huì)意外高出估計(jì)值很多册舞。
布隆過濾器的 error_rate 越小,需要的存儲(chǔ)空間就越大障般,對于不需要過于精確的場合调鲸,error_rate 設(shè)置稍大一點(diǎn)也無傷大雅。比如在新聞去重上而言挽荡,誤判率高一點(diǎn)只會(huì)讓小部分文章不能讓合適的人看到藐石,文章的整體閱讀量不會(huì)因?yàn)檫@點(diǎn)誤判率就帶來巨大的改變。

原理

?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末定拟,一起剝皮案震驚了整個(gè)濱河市于微,隨后出現(xiàn)的幾起案子,更是在濱河造成了極大的恐慌青自,老刑警劉巖株依,帶你破解...
    沈念sama閱讀 218,386評(píng)論 6 506
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件,死亡現(xiàn)場離奇詭異延窜,居然都是意外死亡恋腕,警方通過查閱死者的電腦和手機(jī),發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 93,142評(píng)論 3 394
  • 文/潘曉璐 我一進(jìn)店門需曾,熙熙樓的掌柜王于貴愁眉苦臉地迎上來吗坚,“玉大人,你說我怎么就攤上這事呆万∩淘矗” “怎么了?”我有些...
    開封第一講書人閱讀 164,704評(píng)論 0 353
  • 文/不壞的土叔 我叫張陵谋减,是天一觀的道長牡彻。 經(jīng)常有香客問我,道長出爹,這世上最難降的妖魔是什么庄吼? 我笑而不...
    開封第一講書人閱讀 58,702評(píng)論 1 294
  • 正文 為了忘掉前任,我火速辦了婚禮严就,結(jié)果婚禮上总寻,老公的妹妹穿的比我還像新娘。我一直安慰自己梢为,他們只是感情好渐行,可當(dāng)我...
    茶點(diǎn)故事閱讀 67,716評(píng)論 6 392
  • 文/花漫 我一把揭開白布轰坊。 她就那樣靜靜地躺著,像睡著了一般祟印。 火紅的嫁衣襯著肌膚如雪肴沫。 梳的紋絲不亂的頭發(fā)上,一...
    開封第一講書人閱讀 51,573評(píng)論 1 305
  • 那天蕴忆,我揣著相機(jī)與錄音颤芬,去河邊找鬼。 笑死套鹅,一個(gè)胖子當(dāng)著我的面吹牛站蝠,可吹牛的內(nèi)容都是我干的。 我是一名探鬼主播芋哭,決...
    沈念sama閱讀 40,314評(píng)論 3 418
  • 文/蒼蘭香墨 我猛地睜開眼沉衣,長吁一口氣:“原來是場噩夢啊……” “哼郁副!你這毒婦竟也來了减牺?” 一聲冷哼從身側(cè)響起,我...
    開封第一講書人閱讀 39,230評(píng)論 0 276
  • 序言:老撾萬榮一對情侶失蹤存谎,失蹤者是張志新(化名)和其女友劉穎拔疚,沒想到半個(gè)月后,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體既荚,經(jīng)...
    沈念sama閱讀 45,680評(píng)論 1 314
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡稚失,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 37,873評(píng)論 3 336
  • 正文 我和宋清朗相戀三年,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了恰聘。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片句各。...
    茶點(diǎn)故事閱讀 39,991評(píng)論 1 348
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡,死狀恐怖晴叨,靈堂內(nèi)的尸體忽然破棺而出凿宾,到底是詐尸還是另有隱情,我是刑警寧澤兼蕊,帶...
    沈念sama閱讀 35,706評(píng)論 5 346
  • 正文 年R本政府宣布初厚,位于F島的核電站,受9級(jí)特大地震影響孙技,放射性物質(zhì)發(fā)生泄漏产禾。R本人自食惡果不足惜,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 41,329評(píng)論 3 330
  • 文/蒙蒙 一牵啦、第九天 我趴在偏房一處隱蔽的房頂上張望亚情。 院中可真熱鬧,春花似錦哈雏、人聲如沸楞件。這莊子的主人今日做“春日...
    開封第一講書人閱讀 31,910評(píng)論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽履因。三九已至障簿,卻和暖如春,著一層夾襖步出監(jiān)牢的瞬間栅迄,已是汗流浹背站故。 一陣腳步聲響...
    開封第一講書人閱讀 33,038評(píng)論 1 270
  • 我被黑心中介騙來泰國打工, 沒想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留毅舆,地道東北人西篓。 一個(gè)月前我還...
    沈念sama閱讀 48,158評(píng)論 3 370
  • 正文 我出身青樓,卻偏偏與公主長得像憋活,于是被迫代替她去往敵國和親岂津。 傳聞我的和親對象是個(gè)殘疾皇子,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 44,941評(píng)論 2 355