SpringBoot+Redis實(shí)現(xiàn)布隆過濾器的示例代碼
簡述
關(guān)于布隆過濾器的詳細(xì)介紹,我在這里就不再贅述一遍了
我們首先知道:BloomFilter使用長度為m bit的字節(jié)數(shù)組,使用k個(gè)hash函數(shù),增加一個(gè)元素: 通過k次hash將元素映射到字節(jié)數(shù)組中k個(gè)位置中,并設(shè)置對應(yīng)位置的字節(jié)為1。查詢元素是否存在: 將元素k次hash得到k個(gè)位置,如果對應(yīng)k個(gè)位置的bit是1則認(rèn)為存在,反之則認(rèn)為不存在。
Guava 中已經(jīng)有具體的實(shí)現(xiàn),而在我們實(shí)際生產(chǎn)環(huán)境中,本地的存儲往往無法滿足我們實(shí)際的 需求。所以在這時(shí)候,就需要我們使用 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è)置一個(gè)期望的錯(cuò)誤率和初始大小 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
搭建一個(gè)簡單的 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本身對布隆過濾器就有一個(gè)很好地實(shí)現(xiàn),在 java 端,我們直接導(dǎo)入 redisson 的 jar包即可
<dependency> ? <groupId>org.redisson</groupId> ? <artifactId>redisson</artifactId> ? <version>3.8.2</version> </dependency>
將 Redisson實(shí)例 注入 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ù),為了測試真實(shí),我們記錄了 1000 個(gè) uuid,另外 9000個(gè)作為干擾數(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); ? ? ? ? //因?yàn)檎`差率為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 result
bf_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; ? ? //我們依舊用剛剛的那個(gè)過濾器 ? ? 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); ? ? } ? ? /** ? ? ?* 檢驗(yàn)元素是否可能存在于布隆過濾器中 * @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ù),為了測試真實(shí),我們記錄了 1000 個(gè) uuid,另外 9000個(gè)作為干擾數(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); ? ? ? ? //因?yàn)檎`差率為3%,所以一萬條數(shù)據(jù)wrong的值在30左右 ? ? ? ? System.out.println("wrong:" + wrong); ? ? } }
相比而言,個(gè)人比較推薦第一種,實(shí)現(xiàn)的原理都是差不多,redis 官方已經(jīng)為我封裝好了執(zhí)行腳本,和相關(guān) api,用官方的會更好一點(diǎn)
到此這篇關(guān)于SpringBoot+Redis實(shí)現(xiàn)布隆過濾器的示例代碼的文章就介紹到這了,更多相關(guān)SpringBoot Redis布隆過濾器內(nèi)容請搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!
相關(guān)文章
基于spring boot排除掃描類的三種方式小結(jié)
這篇文章主要介紹了spring boot排除掃描類的三種方式,具有很好的參考價(jià)值,希望對大家有所幫助。如有錯(cuò)誤或未考慮完全的地方,望不吝賜教2021-08-08java hibernate使用注解來定義聯(lián)合主鍵
這篇文章主要介紹了java hibernate使用注解來定義聯(lián)合主鍵的相關(guān)資料,需要的朋友可以參考下2017-01-01Java求字符串中出現(xiàn)次數(shù)最多的字符串以及出現(xiàn)次數(shù)
這篇文章主要為大家詳細(xì)介紹了Java統(tǒng)計(jì)字符串中出現(xiàn)次數(shù)最多的字符串以及出現(xiàn)次數(shù),具有一定的參考價(jià)值,感興趣的小伙伴們可以參考一下2017-04-04java實(shí)現(xiàn)獲取安卓設(shè)備里已安裝的軟件包
本文給大家介紹的是如何獲取設(shè)備中已經(jīng)安裝的應(yīng)用軟件包的代碼,其核心方法原理很簡單,我們通過Android中提供的PackageManager類,來獲取手機(jī)中安裝的應(yīng)用程序信息2015-10-10java操作excel導(dǎo)入導(dǎo)出的3種方式
項(xiàng)目需要,要實(shí)現(xiàn)一個(gè)導(dǎo)入導(dǎo)出excel的功能,于是任務(wù)驅(qū)動著我學(xué)習(xí)到了POI、easypoi和easyexcel這3個(gè)java操作Excel的工具,下面這篇文章主要給大家介紹了關(guān)于java操作excel導(dǎo)入導(dǎo)出的3種方式,需要的朋友可以參考下2023-05-05