SpringBoot+Redis實現(xiàn)布隆過濾器的示例代碼
簡述
關(guān)于布隆過濾器的詳細(xì)介紹,我在這里就不再贅述一遍了
我們首先知道:BloomFilter使用長度為m bit的字節(jié)數(shù)組,使用k個hash函數(shù),增加一個元素: 通過k次hash將元素映射到字節(jié)數(shù)組中k個位置中,并設(shè)置對應(yīng)位置的字節(jié)為1。查詢元素是否存在: 將元素k次hash得到k個位置,如果對應(yīng)k個位置的bit是1則認(rèn)為存在,反之則認(rèn)為不存在。
Guava 中已經(jīng)有具體的實現(xiàn),而在我們實際生產(chǎn)環(huán)境中,本地的存儲往往無法滿足我們實際的 需求。所以在這時候,就需要我們使用 redis 了。
Redis 安裝 Bloom Filter
git clone https://github.com/RedisLabsModules/redisbloom.git cd redisbloom make # 編譯 vi redis.conf ## 增加配置 loadmodule /usr/local/web/redis/RedisBloom-1.1.1/rebloom.so ##redis 重啟 #關(guān)閉 ./redis-cli -h 127.0.0.1 -p 6379 shutdown #啟動 ./redis-server ../redis.conf &
基本指令
#創(chuàng)建布隆過濾器,并設(shè)置一個期望的錯誤率和初始大小 bf.reserve userid 0.01 100000 #往過濾器中添加元素 bf.add userid 'sbc@163.com' #判斷指定key的value是否在bloomfilter里存在,存在:返回1,不存在:返回0 bf.exists userid 'sbc@163.com'
結(jié)合 SpingBoot
搭建一個簡單的 springboot 框架
方式一
配置
<?xml version="1.0" encoding="UTF-8"?> <project xmlns="http://maven.apache.org/POM/4.0.0" ? ? ? ? ?xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" ? ? ? ? xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd"> ? ? <modelVersion>4.0.0</modelVersion> ? ? <groupId>com.bloom</groupId> ? ? <artifactId>test-bloomfilter</artifactId> ? ? <version>1.0-SNAPSHOT</version> ? ? <parent> ? ? ? ? <groupId>org.springframework.boot</groupId> ? ? ? ? <artifactId>spring-boot-starter-parent</artifactId> ? ? ? ? <version>1.5.8.RELEASE</version> ? ? ? ? <relativePath/> <!-- lookup parent from repository --> ? ? </parent> ? ? <dependencies> ? ? ? ? <dependency> ? ? ? ? ? ? <groupId>org.springframework.boot</groupId> ? ? ? ? ? ? <artifactId>spring-boot-starter</artifactId> ? ? ? ? </dependency> ? ? ? ? <dependency> ? ? ? ? ? ? <groupId>org.apache.commons</groupId> ? ? ? ? ? ? <artifactId>commons-lang3</artifactId> ? ? ? ? ? ? <version>3.0.1</version> ? ? ? ? </dependency> ? ? </dependencies> </project>
redis本身對布隆過濾器就有一個很好地實現(xiàn),在 java 端,我們直接導(dǎo)入 redisson 的 jar包即可
<dependency> ? <groupId>org.redisson</groupId> ? <artifactId>redisson</artifactId> ? <version>3.8.2</version> </dependency>
將 Redisson實例 注入 SpringIOC 容器中
@Configuration
public class RedissonConfig {
? ? @Value("${redisson.redis.address}")
? ? private String address;
? ? @Value("${redisson.redis.password}")
? ? private String password;
? ? @Bean
? ? public Config redissionConfig() {
? ? ? ? Config config = new Config();
? ? ? ? SingleServerConfig singleServerConfig = config.useSingleServer();
? ? ? ? singleServerConfig.setAddress(address);
? ? ? ? if (StringUtils.isNotEmpty(password)) {
? ? ? ? ? ? singleServerConfig.setPassword(password);
? ? ? ? }
? ? ? ? return config;
? ? }
? ? @Bean
? ? public RedissonClient redissonClient() {
? ? ? ? return Redisson.create(redissionConfig());
? ? }
}配置文件
redisson.redis.address=redis://127.0.0.1:6379 redisson.redis.password=
最后測試我們的布隆過濾器
@SpringBootApplication
public class BloomApplication {
? ? public static void main(String[] args) {
? ? ? ? ConfigurableApplicationContext context = SpringApplication.run(BloomApplication.class, args);
? ? ? ? RedissonClient redisson = context.getBean(RedissonClient.class);
? ? ? ? RBloomFilter bf = redisson.getBloomFilter("test-bloom-filter");
? ? ? ? bf.tryInit(100000L, 0.03);
? ? ? ? Set<String> set = new HashSet<String>(1000);
? ? ? ? List<String> list = new ArrayList<String>(1000);
? ? ? //向布隆過濾器中填充數(shù)據(jù),為了測試真實,我們記錄了 1000 個 uuid,另外 9000個作為干擾數(shù)據(jù)
? ? ? ? for (int i = 0; i < 10000; i++) {
? ? ? ? ? ?String uuid = UUID.randomUUID().toString();
? ? ? ? ? if(i<1000){
? ? ? ? ? ? set.add(uuid);
? ? ? ? ? ? list.add(uuid);
? ? ? ? ? }
? ? ? ? ??
? ? ? ? ? ?bf.add(uuid);
? ? ? ? }
? ? ? ? int wrong = 0; // 布隆過濾器誤判的次數(shù)
? ? ? ? int right = 0;// 布隆過濾器正確次數(shù)
? ? ? ? for (int i = 0; i < 10000; i++) {
? ? ? ? ? ? String str = i % 10 == 0 ? list.get(i / 10) : UUID.randomUUID().toString();
? ? ? ? ? ? if (bf.contains(str)) {
? ? ? ? ? ? ? ? if (set.contains(str)) {
? ? ? ? ? ? ? ? ? ? right++;
? ? ? ? ? ? ? ? } else {
? ? ? ? ? ? ? ? ? ? wrong++;
? ? ? ? ? ? ? ? }
? ? ? ? ? ? }
? ? ? ? }
? ? ? ? //right 為1000
? ? ? ? System.out.println("right:" + right);
? ? ? ? //因為誤差率為3%,所以一萬條數(shù)據(jù)wrong的值在30左右
? ? ? ? System.out.println("wrong:" + wrong);
? ? ? ?? ?//過濾器剩余空間大小
? ? ? ? System.out.println(bf.count());
? ? }
}以上使我們使用 redisson 的使用方式,下面介紹一種比較原始的方式,使用lua腳本的方式
方式二
bf_add.lua
local bloomName = KEYS[1]
local value = KEYS[2]
local result = redis.call('BF.ADD',bloomName,value)
return resultbf_exist.lua
local bloomName = KEYS[1]
local value = KEYS[2]
local result = redis.call('BF.EXISTS',bloomName,value)
return result@Service
public class RedisBloomFilterService {
? ? @Autowired
? ? private RedisTemplate redisTemplate;
? ? //我們依舊用剛剛的那個過濾器
? ? public static final String BLOOMFILTER_NAME = "test-bloom-filter";
? ? /**
? ? ?* 向布隆過濾器添加元素
? ? ?* @param str
? ? ?* @return
? ? ?*/
? ? public Boolean bloomAdd(String str) {
? ? ? ? DefaultRedisScript<Boolean> LuaScript = new DefaultRedisScript<Boolean>();
? ? ? ? LuaScript.setScriptSource(new ResourceScriptSource(new ClassPathResource("bf_add.lua")));
? ? ? ? LuaScript.setResultType(Boolean.class);
? ? ? ? //封裝傳遞腳本參數(shù)
? ? ? ? List<String> params = new ArrayList<String>();
? ? ? ? params.add(BLOOMFILTER_NAME);
? ? ? ? params.add(str);
? ? ? ? return (Boolean) redisTemplate.execute(LuaScript, params);
? ? }
? ? /**
? ? ?* 檢驗元素是否可能存在于布隆過濾器中 * @param id * @return
? ? ?*/
? ? public Boolean bloomExist(String str) {
? ? ? ? DefaultRedisScript<Boolean> LuaScript = new DefaultRedisScript<Boolean>();
? ? ? ? LuaScript.setScriptSource(new ResourceScriptSource(new ClassPathResource("bf_exist.lua")));
? ? ? ? LuaScript.setResultType(Boolean.class);
? ? ? ? //封裝傳遞腳本參數(shù)
? ? ? ? ArrayList<String> params = new ArrayList<String>();
? ? ? ? params.add(BLOOMFILTER_NAME);
? ? ? ? params.add(String.valueOf(str));
? ? ? ? return (Boolean) redisTemplate.execute(LuaScript, params);
? ? }
}最后我們還是用上面的啟動器執(zhí)行測試代碼
@SpringBootApplication
public class BloomApplication {
? ? public static void main(String[] args) {
? ? ? ? ConfigurableApplicationContext context = SpringApplication.run(BloomApplication.class, args);
? ? ? ? RedisBloomFilterService filterService = context.getBean(RedisBloomFilterService.class);
? ? ? ? Set<String> set = new HashSet<String>(1000);
? ? ? ? List<String> list = new ArrayList<String>(1000);
? ? ? ? //向布隆過濾器中填充數(shù)據(jù),為了測試真實,我們記錄了 1000 個 uuid,另外 9000個作為干擾數(shù)據(jù)
? ? ? ? for (int i = 0; i < 10000; i++) {
? ? ? ? ? ? String uuid = UUID.randomUUID().toString();
? ? ? ? ? ? if (i < 1000) {
? ? ? ? ? ? ? ? set.add(uuid);
? ? ? ? ? ? ? ? list.add(uuid);
? ? ? ? ? ? }
? ? ? ? ? ? filterService.bloomAdd(uuid);
? ? ? ? }
? ? ? ? int wrong = 0; // 布隆過濾器誤判的次數(shù)
? ? ? ? int right = 0;// 布隆過濾器正確次數(shù)
? ? ? ? for (int i = 0; i < 10000; i++) {
? ? ? ? ? ? String str = i % 10 == 0 ? list.get(i / 10) : UUID.randomUUID().toString();
? ? ? ? ? ? if (filterService.bloomExist(str)) {
? ? ? ? ? ? ? ? if (set.contains(str)) {
? ? ? ? ? ? ? ? ? ? right++;
? ? ? ? ? ? ? ? } else {
? ? ? ? ? ? ? ? ? ? wrong++;
? ? ? ? ? ? ? ? }
? ? ? ? ? ? }
? ? ? ? }
? ? ? ? //right 為1000
? ? ? ? System.out.println("right:" + right);
? ? ? ? //因為誤差率為3%,所以一萬條數(shù)據(jù)wrong的值在30左右
? ? ? ? System.out.println("wrong:" + wrong);
? ? }
}相比而言,個人比較推薦第一種,實現(xiàn)的原理都是差不多,redis 官方已經(jīng)為我封裝好了執(zhí)行腳本,和相關(guān) api,用官方的會更好一點(diǎn)
到此這篇關(guān)于SpringBoot+Redis實現(xiàn)布隆過濾器的示例代碼的文章就介紹到這了,更多相關(guān)SpringBoot Redis布隆過濾器內(nèi)容請搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!
相關(guān)文章
SpringBoot中restTemplate請求存在亂碼問題的解決方法
這篇文章主要介紹了SpringBoot中restTemplate請求存在亂碼問題的解決方法,文中有相關(guān)的圖文和代碼示例供大家參考,對大家的解決問題有一定的幫助,需要的朋友可以參考下2024-11-11
alibaba?seata服務(wù)端具體實現(xiàn)
seata是來處理分布式服務(wù)之間互相調(diào)用的事務(wù)問題,本文重點(diǎn)給大家介紹alibaba-seata實現(xiàn)方法,文中通過實例代碼給大家介紹的非常詳細(xì),對大家的學(xué)習(xí)或工作具有一定的參考借鑒價值,需要的朋友可以參考下2022-02-02
JAVA微信掃碼支付模式二線上支付功能實現(xiàn)以及回調(diào)
本篇文章主要介紹了JAVA微信掃碼支付模式二線上支付功能實現(xiàn)以及回調(diào),這里整理了詳細(xì)的代碼,有需要的小伙伴可以參考下。2016-11-11
Maven實現(xiàn)把項目依賴的所有jar包都打到同一個jar中
文章介紹了Maven打包項目的方法,包括使用shade-plugin和assembly-plugin插件配置、處理生成jar包中的簽名文件錯誤、手動生成可執(zhí)行jar包需指定主類,以及Linux下運(yùn)行jar包的多種方式(前臺、后臺、nohup等)2025-08-08
利用Lambda表達(dá)式創(chuàng)建新線程案例
這篇文章主要介紹了利用Lambda表達(dá)式創(chuàng)建新線程案例,具有很好的參考價值,希望對大家有所幫助。一起跟隨小編過來看看吧2020-08-08

