Java SpringBoot使用guava過濾器
介紹
布隆過濾器(BloomFilter)是一種數(shù)據(jù)結(jié)構(gòu),特點(diǎn)是高效地插入和查詢,可以用來告訴你某個(gè)key一定不存在或者可能存在,它是用多個(gè)哈希函數(shù),將一個(gè)key映射到多個(gè)數(shù)組下標(biāo)中。
特點(diǎn)
布隆過濾器可以告訴我們 "某樣?xùn)|西一定不存在或者可能存在",也就是說布隆過濾器說這個(gè)key不存在則一定不存在,布隆過濾器說這個(gè)key存在則可能不存在(會(huì)存在誤判),利用這個(gè)判斷是否存在的特點(diǎn)可以做很多有趣的事情。
數(shù)據(jù)結(jié)構(gòu)
由一串很長(zhǎng)的二進(jìn)制數(shù)組向量組成,可以將其看成一個(gè)二進(jìn)制數(shù)組。既然是二進(jìn)制,那么里面存放的不是0,就是1,但是初始默認(rèn)值都是0。
新增數(shù)據(jù)
把一個(gè)key經(jīng)過多個(gè)hash函數(shù)計(jì)算出多個(gè)數(shù)組下標(biāo),把計(jì)算出的下標(biāo)的位置的0變成1。
查詢數(shù)據(jù)
當(dāng)要查詢一個(gè)key是否在布隆過濾器中,用相同的方法計(jì)算出多個(gè)1,判斷根據(jù)這個(gè)key計(jì)算出的多個(gè)1的位置在布隆過濾器中是否都是1,如果都是那么代表可能存在,如果不都是代表一定不存在。
刪除數(shù)據(jù)
布隆過濾器很難進(jìn)行刪除操作。
作用
高效判斷一個(gè)數(shù)據(jù)在不在某一組數(shù)據(jù)中。
誤差率: 創(chuàng)建布隆過濾器時(shí),可以設(shè)置誤差率,誤差率越小,hash函數(shù)越多,二進(jìn)制數(shù)據(jù)占用空間越大。hash函數(shù)越多,計(jì)算出的hash值多,對(duì)應(yīng)計(jì)算出的二進(jìn)制數(shù)組下標(biāo)越多,效率越低,有一個(gè)位置在布隆過濾器中置不為1,就代表不存在,精確度越高。
業(yè)務(wù)使用
布隆過濾器就是在Redis緩存前面在加一層過濾,負(fù)責(zé)過濾無效的請(qǐng)求,如果請(qǐng)求沒有通過布隆過濾器就不會(huì)到緩存和數(shù)據(jù)庫。
布隆過濾器的優(yōu)點(diǎn) :
- 空間效率 和 查詢效率都遠(yuǎn)遠(yuǎn)超過一般的算法。
- 增加和查詢?cè)氐臅r(shí)間復(fù)雜為O(N)(N為哈希函數(shù)的個(gè)數(shù),通常情況比較?。?。
- 占用空間小,如果允許存在一定的誤判,布隆過濾器是非常節(jié)省空間。
- 保密性強(qiáng),布隆過濾器不存儲(chǔ)數(shù)據(jù)本身。
布隆過濾器的缺點(diǎn): 有點(diǎn)一定的誤判率,但是可以通過調(diào)整參數(shù)來降低。
- 無法獲取元素本身。
- 很難刪除元素。
快速開始
導(dǎo)入依賴:
<dependency> <groupId>com.google.guava</groupId> <artifactId>guava</artifactId> <version>28.0-jre</version> </dependency>
配置類:
把布隆過濾器對(duì)象注入進(jìn)IOC容器。
import com.google.common.hash.BloomFilter; import com.google.common.hash.Funnels; import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Configuration; import java.nio.charset.Charset; @Configuration public class BloomFilterConfig { /** * expectedInsertions:期望添加的數(shù)據(jù)個(gè)數(shù) * fpp:期望的誤判率,期望的誤判率越低,布隆過濾器計(jì)算時(shí)間越長(zhǎng) * @return */ @Bean public BloomFilter<String> goodsIDBloom(){ BloomFilter<String> filter = BloomFilter.create(Funnels.stringFunnel(Charset.forName("utf-8")), 1000,0.00001); return filter; } @Bean public BloomFilter<String> orderBloom(){ BloomFilter<String> filter = BloomFilter.create(Funnels.stringFunnel(Charset.forName("utf-8")), 1000,0.00001); return filter; } }
啟動(dòng)項(xiàng)目時(shí)把所有key插入布隆過濾器:
import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper; import com.google.common.hash.BloomFilter; import com.liu.seckill.entity.SeckillGood; import com.liu.seckill.service.SeckillGoodService; import com.liu.seckill.service.UserService; import lombok.extern.slf4j.Slf4j; import org.springframework.beans.factory.InitializingBean; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.beans.factory.annotation.Qualifier; import org.springframework.context.annotation.Configuration; import org.springframework.data.redis.core.RedisTemplate; import org.springframework.scheduling.annotation.Scheduled; import java.time.Duration; import java.time.LocalDateTime; import java.util.ArrayList; import java.util.HashMap; import java.util.List; import java.util.Map; import java.util.concurrent.TimeUnit; @Slf4j @Configuration public class InitConfig implements InitializingBean { @Autowired UserService userService; @Autowired SeckillGoodService seckillGoodService; @Autowired RedisTemplate redisTemplate; @Autowired RedisConfig redisConfig; @Autowired @Qualifier("goodsIDBloom") BloomFilter<String> goodsIDBloom; /** * 把商品庫存加載到 Redis中 * 每天更新 * @throws Exception */ @Override @Scheduled(fixedRate = 1000*60*60*24) public void afterPropertiesSet() throws Exception { //只緩存秒殺還沒結(jié)束或秒殺還沒開始的商品 List<SeckillGood> seckillGoods = seckillGoodService.list(new QueryWrapper<SeckillGood>().ge("end_time", LocalDateTime.now())); if (seckillGoods == null) { log.info("暫無秒殺商品"); return; } //將秒殺商品ID和庫存分別存入redis中 List<Long> seckillGoodIDList = new ArrayList<>(); for (SeckillGood seckillGood : seckillGoods) { seckillGoodIDList.add(seckillGood.getSgId()); //設(shè)置過期時(shí)間 long millis = Duration.between(LocalDateTime.now(), seckillGood.getEndTime()).toMillis(); redisTemplate.opsForValue().set("seckillGoodID:" + seckillGood.getSgId(), seckillGood.getSgStock(), millis, TimeUnit.MILLISECONDS); //存儲(chǔ)值到布隆過濾器中 goodsIDBloom.put(seckillGood.getSgId()+""); } redisTemplate.opsForValue().set("seckillGoodIDList:", seckillGoodIDList,1,TimeUnit.DAYS); } }
使用布隆過濾器:
查詢r(jià)edis之前先走布隆過濾器,如果布隆過濾器判斷沒有則一定沒有。
@RestController @RequestMapping("/seckillOrder") public class SeckillOrderController { //如果有多個(gè)布隆過濾器,就同時(shí)使用@Qualifier和@Autowired @Autowired @Qualifier("goodsIDBloom") BloomFilter<String> goodsIDBloom; @Autowired @Qualifier("orderBloom") BloomFilter<String> orderBloom; @GetMapping("/seckillGoods/{goodId}") public Result seckillGoods(@PathVariable("goodId") Long goodId) { ValueOperations valueOperations = redisTemplate.opsForValue(); //判斷秒殺商品是否存在 //如果商品id在布隆過濾器中存在,那么就要再去判斷在不在redis中,在,才能證明真的在 if (goodsIDBloom.mightContain(goodId+"")){ ArrayList<Long> seckillGoodIDList = (ArrayList<Long>) valueOperations.get("seckillGoodIDList:"); Assert.isTrue(seckillGoodIDList.contains(goodId),"未找到該商品,商品ID有誤或此商品不參與秒殺或此商品秒殺已結(jié)束"); }else { Assert.isTrue(false,"未找到該商品,商品ID有誤或此商品不參與秒殺或此商品秒殺已結(jié)束"); } } }
到此這篇關(guān)于Java SpringBoot使用guava的布隆過濾器的文章就介紹到這了,更多相關(guān)SpringBoot使用guava 內(nèi)容請(qǐng)搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!
相關(guān)文章
java實(shí)現(xiàn)坦克大戰(zhàn)小游戲
這篇文章主要為大家詳細(xì)介紹了java實(shí)現(xiàn)坦克大戰(zhàn)小游戲,文中示例代碼介紹的非常詳細(xì),具有一定的參考價(jià)值,感興趣的小伙伴們可以參考一下2021-01-01Java移除無效括號(hào)的方法實(shí)現(xiàn)
本文主要介紹了Java移除無效括號(hào)的方法實(shí)現(xiàn),文中通過示例代碼介紹的非常詳細(xì),具有一定的參考價(jià)值,感興趣的小伙伴們可以參考一下2021-08-08Java實(shí)現(xiàn)滑動(dòng)驗(yàn)證碼生成(后端工具類)
這篇文章主要為大家詳細(xì)介紹了Java實(shí)現(xiàn)滑動(dòng)驗(yàn)證碼生成功能中的后端工具類部分,文中的示例代碼講解詳細(xì),感興趣的小伙伴可以跟隨小編一起學(xué)習(xí)一下2022-10-10Jenkins?Pipline實(shí)現(xiàn)及原理示例解析
這篇文章主要為大家介紹了Jenkins?Pipline實(shí)現(xiàn)及原理示例解析,有需要的朋友可以借鑒參考下,希望能夠有所幫助,祝大家多多進(jìn)步,早日升職加薪2023-07-07Java Math類的三個(gè)方法ceil,floor,round用法
這篇文章主要介紹了Java Math類的三個(gè)方法ceil,floor,round用法,具有很好的參考價(jià)值,希望對(duì)大家有所幫助。如有錯(cuò)誤或未考慮完全的地方,望不吝賜教2021-07-07java的GUI實(shí)現(xiàn)簡(jiǎn)單切換界面
這篇文章主要為大家詳細(xì)介紹了java的GUI實(shí)現(xiàn)簡(jiǎn)單切換界面,文中示例代碼介紹的非常詳細(xì),具有一定的參考價(jià)值,感興趣的小伙伴們可以參考一下2022-04-04Spring Boot Actuator端點(diǎn)相關(guān)原理解析
這篇文章主要介紹了Spring Boot Actuator端點(diǎn)相關(guān)原理解析,文中通過示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友可以參考下2020-07-07mybatis.type-aliases-package之巨坑的解決
這篇文章主要介紹了mybatis.type-aliases-package之巨坑的解決,具有很好的參考價(jià)值,希望對(duì)大家有所幫助。2021-09-09