實現布隆過濾器的三種方式

tuonas發表於2020-10-09

一、谷歌布隆過濾器

專案地址:https://gitee.com/quancong/redis.git
BloomFilter專案

需求:判斷是否是已註冊過的使用者

1、引入布隆過濾器pom依賴

    <!-- 谷歌布隆過濾器-->
     <dependency>
         <groupId>com.google.guava</groupId>
         <artifactId>guava</artifactId>
         <version>21.0</version>
    </dependency>

2、資料庫中表資料

SET NAMES utf8mb4;
SET FOREIGN_KEY_CHECKS = 0;

DROP TABLE IF EXISTS `user`;
CREATE TABLE `user` (
  `id` int NOT NULL AUTO_INCREMENT COMMENT '主鍵',
  `username` varchar(20) CHARACTER SET utf8mb4 COLLATE utf8mb4_0900_ai_ci NOT NULL DEFAULT '' COMMENT '使用者名稱',
  `password` varchar(20) NOT NULL DEFAULT '' COMMENT '使用者建立時間',
  PRIMARY KEY (`id`)
) ENGINE=InnoDB AUTO_INCREMENT=4 DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_0900_ai_ci;

BEGIN;
INSERT INTO `user` VALUES (1, '張三', '2020-07-31 17:08:42');
INSERT INTO `user` VALUES (2, '李四', '2020-07-31 07:10:14');
INSERT INTO `user` VALUES (3, '王五', '2020-07-31 20:30:09');
COMMIT;

SET FOREIGN_KEY_CHECKS = 1;

在這裡插入圖片描述

3、啟動springboot專案時將使用者id新增到布隆過濾器中來

/**
 * 啟動時初始化布隆過濾器
 */
@Service
public class BloomFilterService implements ApplicationRunner {

    @Autowired
    private UserDao userDao;

    private BloomFilter<Integer> bf;

    /**
     * 啟動時將資料庫中使用者id載入到布隆過濾器中來
     * @param args
     * @throws Exception
     */
    @Override
    public void run(ApplicationArguments args) throws Exception {
        List<Integer> userIdList = userDao.findAllUserId();
        if (CollectionUtils.isEmpty(userIdList)) return;
        //建立布隆過濾器 誤判率預設為3%
        bf = BloomFilter.create(Funnels.integerFunnel(),userIdList.size());
        for (Integer userId : userIdList) {
            bf.put(userId);
        }
    }

    /***
     * 判斷id可能存在於布隆過濾器裡面
     * @param id
     * @return
     */
    public boolean userIdExists(int id){
        return bf.mightContain(id);
    }
}

這裡同時還有一個判斷使用者id可能存在的方法

4、編寫controller測試一下

@RestController
public class UserController {

    @Autowired
    private BloomFilterService bloomFilterService;

    @RequestMapping("/bloom/idExists")
    public boolean ifExists(int id){
        return bloomFilterService.userIdExists(id);
    }
}

瀏覽器輸入http://localhost:8080/bloom/idExists?id=1,即一個已存在的使用者id
在這裡插入圖片描述
輸入一個存在的使用者id返回true

在這裡插入圖片描述
輸入一個不存在的使用者id返回false

二、redis原生setbit命令實現

因篇幅過長,可參見我的另一篇部落格。

https://blog.csdn.net/qq_41963899/article/details/108911244

三、使用redis布隆過濾器外掛實現

1、安裝布隆過濾器

git clone git://github.com/RedisLabsModules/rebloom
cd rebloom
make

2、有一個redisbloom.so檔案,複製路徑,假如為path,在redis.conf中指定該模組

loadmodule /path/rebloom.so

3、重啟redis

redis-server  ./redis.conf

4、測試

redis-cli bf.add myBloom redis1 # 向myBloom布隆過濾器新增redis1鍵
redis-cli bf.add myBloom redis2
redis-cli # 進入redis客戶端

判斷yBloom布隆過濾器是否存在redis1、redis2、redis3這些鍵

bf.exists myBloom redis1
bf.exists myBloom redis2
bf.exists myBloom redis3

5、springboot整合redis布隆過濾器

專案原始碼:https://gitee.com/quancong/redis.git

@Service
public class RedisBloomFilter {

    @Autowired
    private RedisTemplate redisTemplate;
    
    public Boolean bloomFilterAdd(String filterName,int value){
        DefaultRedisScript<Boolean> bloomAdd = new DefaultRedisScript<>();
        bloomAdd.setScriptSource(new ResourceScriptSource(new ClassPathResource("bloomFilterAdd.lua")));
        bloomAdd.setResultType(Boolean.class);
        List<Object> keyList= new ArrayList<>();
        keyList.add(filterName);
        keyList.add(value+"");
        Boolean result = (Boolean) redisTemplate.execute(bloomAdd,keyList);
        return result;
    }
    
    public Boolean bloomFilterExists(String filterName,int value){
        DefaultRedisScript<Boolean> bloomExists= new DefaultRedisScript<>();
        bloomExists.setScriptSource(new ResourceScriptSource(new ClassPathResource("bloomFilterExist.lua")));
        bloomExists.setResultType(Boolean.class);
        List<Object> keyList= new ArrayList<>();
        keyList.add(filterName);
        keyList.add(value+"");
        Boolean result = (Boolean) redisTemplate.execute(bloomExists,keyList);
        return result;
    }
}

測試一下:

@RestController
public class RedisController {

    @Autowired
    private RedisBloomFilter redisBloomFilter;

    @RequestMapping("/bloom/redisIdExists")
    public boolean redisidExists(int id){
        return redisBloomFilter.bloomFilterExists("myBloom",id);
    }

    @RequestMapping("/bloom/redisIdAdd")
    public boolean redisidAdd(int id){
        return redisBloomFilter.bloomFilterAdd("myBloom",id);
    }

}

先往布隆過濾器中新增兩條資料

http://localhost:8080/bloom/redisIdAdd?id=1

在這裡插入圖片描述
在這裡插入圖片描述
再測試一下redis布隆過濾器不存在的資料

http://localhost:8080/bloom/redisIdExists?id=3

在這裡插入圖片描述
成功!

相關文章