1. 程式人生 > 程式設計 >Redis Module 實現布隆過濾器

Redis Module 實現布隆過濾器

Redis Module

Redis module 是Redis 4.0 以後支援的新的特性,這裡很多國外牛逼的大學和機構提供了很多牛逼的Module 只要編譯引入到Redis 中就能輕鬆的實現我們某些需求的功能。在Redis 官方Module 中有一些我們常見的一些模組,我們在這裡就做一個簡單的使用。

個人推薦可以嘗試的

  • neural-redis 主要是神經網路的機器學,整合到redis 可以做一些機器訓練感興趣的可以嘗試
  • RedisSearch 主要支援一些富文字的的搜尋
  • RedisBloom 支援分散式環境下的Bloom 過濾器

布隆過濾器

比如我們存放某些資料的id屬性,id屬性通過K個Hash 函式,計算到bit(位陣列)上面,把它們變成1。檢索時,我們只要看看這些點是不是都是1就(大約)知道集合中有沒有它了:如果這些點有任何一個0,則被檢元素一定不在;如果都是1,則被檢元素很可能在。這就是布隆過濾器的基本思想。這個一般可以使用在新聞推薦和快取穿透的處理上面,效果很好。

優點和缺點

優點

空間效率和查詢時間都遠遠超過一般的演演算法,布隆過濾器儲存空間和插入 / 查詢時間都是常數O(k)。 另外,雜湊函式相互之間沒有關係,方便由硬體並行實現。 布隆過濾器不需要儲存元素本身,在某些對保密要求非常嚴格的場合有優勢。

缺點

  • 為什麼效率會那麼高,是因為布隆過濾器犧牲了準備性和刪除操作,布隆過濾器存在一定的誤判概率。 比如我們查詢某個key ,通過K個hash 函式對應的位陣列上面對映的都是1 ,但其實我們的資料儲存並沒有這個資料,所以存在一定誤判。如果我們用來做一個ip 黑名單的新增,可能就會存在一定的誤判率。
  • 刪除問題,我們如果把陣列某個位置設定為 1 或者是 0,那麼識別會影響其它資料的判斷,

實現

而對於我們java 實現的bloom過濾器可以通過谷歌提供的guava ,但是我們現在的系統大部分都是分散式系統,那麼這種方式就不太合適,我們可能會想把資料存放到一個分散式環境下的記憶體中,自然就會想到redis,那麼redis的那幫大神們自然肯定也會想到,所以就有很多開源組織和個人研發了基於redis 的布隆過濾器。 那麼首先介紹使用Redis Module 整合進RedisBloom。

  1. 下載官方的git 倉庫檔案 github.com/RedisBloom/…
  2. 比如在Linux 伺服器上面可以直接通過git 拉取原始碼 git github.com/RedisBloom/…
  3. 進入下載好的目錄 執行make 命令進行編譯成so 庫

  1. 接下來就很簡單了,直接在redis.conf 配置檔案裡面,然後重啟伺服器
    loadmodule /opt/redis/redis-cluster/redisbloom.so
  2. 重啟服務,進入 client 檢視整合進來的module

相關bf 命令可以去https://github.com/RedisBloom/RedisBloom 檢視

那麼對與官網提供的其它模組的整合模式基本和RedisBloom一樣,有興趣的可以去嘗試下,這樣面試的時候可以提一下自己研究過這塊,相信面試官會喜歡這樣的面試人員。

那麼如果用java 呼叫RedisBloom 先給大家看一下官網的實現方式。

// 核心還是通過jedis客戶端的方式,這種方式還是筆者看它原始碼改寫後的,官方提供的不支援redis 密碼驗證
pom 檔案
<dependency>
    <groupId>redis.clients</groupId>
    <artifactId>jedis</artifactId>
    <version>3.0.0</version>
</dependency>

<dependency>
    <groupId>com.redislabs</groupId>
    <artifactId>jrebloom</artifactId>
    <version>2.0.0-SNAPSHOT</version>
</dependency>
<repositories>
    <repository>
        <id>snapshots-repo</id>
        <url>https://oss.sonatype.org/content/repositories/snapshots</url>
    </repository>
</repositories>

程式碼實現

public class RedisBloom {
    public static void main(String[] args) {
        JedisPoolConfig conf = new JedisPoolConfig();
        conf.setMaxTotal(100);
        conf.setTestOnBorrow(false);
        conf.setTestOnReturn(false);
        conf.setTestOnCreate(false);
        conf.setTestWhileIdle(false);
        conf.setMinEvictableIdleTimeMillis(60000L);
        conf.setTimeBetweenEvictionRunsMillis(30000L);
        conf.setNumTestsPerEvictionRun(-1);
        conf.setFairness(true);
        JedisPool jedisPool = new JedisPool(conf,"192.168.13.131",6379,30000,"123456");
        Client client = new Client(jedisPool);

        boolean exists = client.exists("newFilter","foo55");
        System.out.println(exists);
        client.add("newFilter","123");
        client.add("newFilter","456");
        client.add("newFilter","789");
        client.close();
    }
}
複製程式碼

上面的這種實現方式比較複雜,下面給大家介紹一款redis 比較牛皮的客戶端工具,可能大家聽說過 Redission 框架,可能大家瞭解的是它實現分散式鎖忒簡單,這個框架提供了很多強大的功能, 比如分散式鎖、令牌桶限流、分散式布隆過濾器,那麼我們就來爽一波

  <!-- 引入redisson -->
        <dependency>
            <groupId>org.redisson</groupId>
            <artifactId>redisson</artifactId>
            <version>3.11.5</version>
        </dependency>
   
 
 編寫 ReissonConfig
   /**
 * @Description
 * @Author gavin
 * @Since 1.0
 * @Date 2019/10/27
 */
@Configuration
public class RedissonConfig  {

    @Bean
    public RedissonClient redissonClient(){
        Config config = new Config();
        config.useSingleServer().setAddress("redis://192.168.13.132:6379")
        return Redisson.create(config);
    }
}
   
複製程式碼
// 分散式訊號實現秒殺庫存  利用ab 測試工具可以測試
    @GetMapping("/kill2")
    public void kill2(){
        RSemaphore semaphore = redissonClient.getSemaphore("number");
        // 預設搶到會減少一個
        boolean tryAcquire = semaphore.tryAcquire();
        if(tryAcquire){
            Integer number = redisUtil.get("number",Integer.class);
            if(number > 0){
                System.out.println("當前庫存還剩:"+number+"   使用者搶購成功");
            }
        }
    }
// 分散式環境下布隆過濾器的實現,非常的簡單    
 @Test
 public void testBloomFilter() {
        RBloomFilter<String> bloomFilter = redissonClient.getBloomFilter("newFilter2");
        // 初始化布隆過濾器,預計統計元素數量為55000000,期望誤差率為0.03
        bloomFilter.tryInit(55000000L,0.03);
        bloomFilter.add("lisi");
        bloomFilter.add("wangwu");
        boolean foo = bloomFilter.contains("lisi343");
        System.out.println(foo);
    }
    
//  限流

  public void testRateLimiter() {
        RRateLimiter rateLimiter = redissonClient.getRateLimiter("myRateLimiter");
        // 初始化
        // 最大流速 = 每1秒鐘產生10個令牌
        boolean trySetRate = rateLimiter.trySetRate(RateType.OVERALL,10,1,RateIntervalUnit.SECONDS);
        if(rateLimiter.tryAcquire()){
            System.out.println("我獲取到了資訊哈");
        }else{
            System.out.println("對不起你被限流了哈");
        }
    }
複製程式碼

歐克歐克? 大概這裡都介紹完畢了,如果大家想研究更多Redisson 的強大功能,這裡給一個傳送地址 github.com/redisson/re… 絕壁不會讓你失望