天天看點

java 布隆過濾器_Redis詳解(十三)------ Redis布隆過濾器

大家好,我是可樂,一個專注原創,樂于分享的程式猿。 本系列教程持續更新,可以微信搜尋「 IT可樂 」第一時間閱讀。回複《電子書》有我為大家特别篩選的海量免費書籍資料

本篇部落格我們主要介紹如何用Redis實作布隆過濾器,但是在介紹布隆過濾器之前,我們首先介紹一下,為啥要使用布隆過濾器。

1、布隆過濾器使用場景

比如有如下幾個需求:

①、原本有10億個号碼,現在又來了10萬個号碼,要快速準确判斷這10萬個号碼是否在10億個号碼庫中?

解決辦法一:将10億個号碼存入資料庫中,進行資料庫查詢,準确性有了,但是速度會比較慢。

解決辦法二:将10億号碼放入記憶體中,比如Redis緩存中,這裡我們算一下占用記憶體大小:10億*8位元組=8GB,通過記憶體查詢,準确性和速度都有了,但是大約8gb的記憶體空間,挺浪費記憶體空間的。

②、接觸過爬蟲的,應該有這麼一個需求,需要爬蟲的網站千千萬萬,對于一個新的網站url,我們如何判斷這個url我們是否已經爬過了?

解決辦法還是上面的兩種,很顯然,都不太好。

③、同理還有垃圾郵箱的過濾。

那麼對于類似這種,大資料量集合,如何準确快速的判斷某個資料是否在大資料量集合中,并且不占用記憶體,布隆過濾器應運而生了。

2、布隆過濾器簡介

帶着上面的幾個疑問,我們來看看到底什麼是布隆過濾器。

布隆過濾器:一種資料結構,是由一串很長的二進制向量組成,可以将其看成一個二進制數組。既然是二進制,那麼裡面存放的不是0,就是1,但是初始預設值都是0。

如下所示:

java 布隆過濾器_Redis詳解(十三)------ Redis布隆過濾器
①、添加資料

介紹概念的時候,我們說可以将布隆過濾器看成一個容器,那麼如何向布隆過濾器中添加一個資料呢?

如下圖所示:當要向布隆過濾器中添加一個元素key時,我們通過多個hash函數,算出一個值,然後将這個值所在的方格置為1。

比如,下圖hash1(key)=1,那麼在第2個格子将0變為1(數組是從0開始計數的),hash2(key)=7,那麼将第8個格子置位1,依次類推。

java 布隆過濾器_Redis詳解(十三)------ Redis布隆過濾器
②、判斷資料是否存在?

知道了如何向布隆過濾器中添加一個資料,那麼新來一個資料,我們如何判斷其是否存在于這個布隆過濾器中呢?

很簡單,我們隻需要将這個新的資料通過上面自定義的幾個哈希函數,分别算出各個值,然後看其對應的地方是否都是1,如果存在一個不是1的情況,那麼我們可以說,該新資料一定不存在于這個布隆過濾器中。

反過來說,如果通過哈希函數算出來的值,對應的地方都是1,那麼我們能夠肯定的得出:這個資料一定存在于這個布隆過濾器中嗎?

答案是否定的,因為多個不同的資料通過hash函數算出來的結果是會有重複的,是以會存在某個位置是别的資料通過hash函數置為的1。

我們可以得到一個結論:布隆過濾器可以判斷某個資料一定不存在,但是無法判斷一定存在。

③、布隆過濾器優缺點

優點:優點很明顯,二進制組成的數組,占用記憶體極少,并且插入和查詢速度都足夠快。

缺點:随着資料的增加,誤判率會增加;還有無法判斷資料一定存在;另外還有一個重要缺點,無法删除資料。

3、Redis實作布隆過濾器

①、bitmaps

  我們知道計算機是以二進制位作為底層存儲的基礎機關,一個位元組等于8位。

比如“big”字元串是由三個字元組成的,這三個字元對應的ASCII碼分為是98、105、103,對應的二進制存儲如下:

java 布隆過濾器_Redis詳解(十三)------ Redis布隆過濾器

  在Redis中,Bitmaps 提供了一套指令用來操作類似上面字元串中的每一個位。

一、設定值

setbit key offset value
java 布隆過濾器_Redis詳解(十三)------ Redis布隆過濾器

  我們知道"b"的二進制表示為0110 0010,我們将第7位(從0開始)設定為1,那0110 0011 表示的就是字元“c”,是以最後的字元 “big”變成了“cig”。

二、擷取值

gitbit key offset
java 布隆過濾器_Redis詳解(十三)------ Redis布隆過濾器

  三、擷取位圖指定範圍值為1的個數

bitcount key [start end]

如果不指定,那就是擷取全部值為1的個數。

注意:start和end指定的是位元組的個數,而不是位數組下标。

java 布隆過濾器_Redis詳解(十三)------ Redis布隆過濾器
②、Redisson

  Redis 實作布隆過濾器的底層就是通過 bitmap 這種資料結構,至于如何實作,這裡就不重複造輪子了,介紹業界比較好用的一個用戶端工具——Redisson。

Redisson 是用于在 Java 程式中操作 Redis 的庫,利用Redisson 我們可以在程式中輕松地使用 Redis。

下面我們就通過 Redisson 來構造布隆過濾器。

package com.ys.rediscluster.bloomfilter.redisson;

import org.redisson.Redisson;
import org.redisson.api.RBloomFilter;
import org.redisson.api.RedissonClient;
import org.redisson.config.Config;

public class RedissonBloomFilter {

    public static void main(String[] args) {
        Config config = new Config();
        config.useSingleServer().setAddress("redis://192.168.14.104:6379");
        config.useSingleServer().setPassword("123");
        //構造Redisson
        RedissonClient redisson = Redisson.create(config);

        RBloomFilter<String> bloomFilter = redisson.getBloomFilter("phoneList");
        //初始化布隆過濾器:預計元素為100000000L,誤差率為3%
        bloomFilter.tryInit(100000000L,0.03);
        //将号碼10086插入到布隆過濾器中
        bloomFilter.add("10086");

        //判斷下面号碼是否在布隆過濾器中
        System.out.println(bloomFilter.contains("123456"));//false
        System.out.println(bloomFilter.contains("10086"));//true
    }
}
           

這是單節點的Redis實作方式,如果資料量比較大,期望的誤差率又很低,那單節點所提供的記憶體是無法滿足的,這時候可以使用分布式布隆過濾器,同樣也可以用 Redisson 來實作,這裡我就不做代碼示範了,大家有興趣可以試試。

4、guava 工具

最後提一下不用Redis如何來實作布隆過濾器。

guava 工具包相信大家都用過,這是谷歌公司提供的,裡面也提供了布隆過濾器的實作。

package com.ys.rediscluster.bloomfilter;

import com.google.common.base.Charsets;
import com.google.common.hash.BloomFilter;
import com.google.common.hash.Funnel;
import com.google.common.hash.Funnels;

public class GuavaBloomFilter {
    public static void main(String[] args) {
        BloomFilter<String> bloomFilter = BloomFilter.create(Funnels.stringFunnel(Charsets.UTF_8),100000,0.01);

        bloomFilter.put("10086");

        System.out.println(bloomFilter.mightContain("123456"));
        System.out.println(bloomFilter.mightContain("10086"));
    }
}
           
本系列教程持續更新,可以微信搜尋「 IT可樂 」第一時間閱讀。回複《電子書》有我為大家特别篩選的書籍資料

http://weixin.qq.com/r/iSqbg-fEGqplrbPg93_b (二維碼自動識别)