Java SpringBoot使用guava過濾器
介紹
布隆過濾器(BloomFilter)是一種數(shù)據(jù)結構,特點是高效地插入和查詢,可以用來告訴你某個key一定不存在或者可能存在,它是用多個哈希函數(shù),將一個key映射到多個數(shù)組下標中。
特點
布隆過濾器可以告訴我們 "某樣東西一定不存在或者可能存在",也就是說布隆過濾器說這個key不存在則一定不存在,布隆過濾器說這個key存在則可能不存在(會存在誤判),利用這個判斷是否存在的特點可以做很多有趣的事情。
數(shù)據(jù)結構
由一串很長的二進制數(shù)組向量組成,可以將其看成一個二進制數(shù)組。既然是二進制,那么里面存放的不是0,就是1,但是初始默認值都是0。
新增數(shù)據(jù)

把一個key經(jīng)過多個hash函數(shù)計算出多個數(shù)組下標,把計算出的下標的位置的0變成1。
查詢數(shù)據(jù)
當要查詢一個key是否在布隆過濾器中,用相同的方法計算出多個1,判斷根據(jù)這個key計算出的多個1的位置在布隆過濾器中是否都是1,如果都是那么代表可能存在,如果不都是代表一定不存在。
刪除數(shù)據(jù)
布隆過濾器很難進行刪除操作。
作用
高效判斷一個數(shù)據(jù)在不在某一組數(shù)據(jù)中。
誤差率: 創(chuàng)建布隆過濾器時,可以設置誤差率,誤差率越小,hash函數(shù)越多,二進制數(shù)據(jù)占用空間越大。hash函數(shù)越多,計算出的hash值多,對應計算出的二進制數(shù)組下標越多,效率越低,有一個位置在布隆過濾器中置不為1,就代表不存在,精確度越高。
業(yè)務使用
布隆過濾器就是在Redis緩存前面在加一層過濾,負責過濾無效的請求,如果請求沒有通過布隆過濾器就不會到緩存和數(shù)據(jù)庫。
布隆過濾器的優(yōu)點 :
- 空間效率 和 查詢效率都遠遠超過一般的算法。
- 增加和查詢元素的時間復雜為O(N)(N為哈希函數(shù)的個數(shù),通常情況比較?。?/li>
- 占用空間小,如果允許存在一定的誤判,布隆過濾器是非常節(jié)省空間。
- 保密性強,布隆過濾器不存儲數(shù)據(jù)本身。
布隆過濾器的缺點: 有點一定的誤判率,但是可以通過調(diào)整參數(shù)來降低。
- 無法獲取元素本身。
- 很難刪除元素。
快速開始
導入依賴:
<dependency>
<groupId>com.google.guava</groupId>
<artifactId>guava</artifactId>
<version>28.0-jre</version>
</dependency>配置類:
把布隆過濾器對象注入進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ù)個數(shù)
* fpp:期望的誤判率,期望的誤判率越低,布隆過濾器計算時間越長
* @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;
}
}啟動項目時把所有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 {
//只緩存秒殺還沒結束或秒殺還沒開始的商品
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());
//設置過期時間
long millis = Duration.between(LocalDateTime.now(), seckillGood.getEndTime()).toMillis();
redisTemplate.opsForValue().set("seckillGoodID:" + seckillGood.getSgId(), seckillGood.getSgStock(), millis, TimeUnit.MILLISECONDS);
//存儲值到布隆過濾器中
goodsIDBloom.put(seckillGood.getSgId()+"");
}
redisTemplate.opsForValue().set("seckillGoodIDList:", seckillGoodIDList,1,TimeUnit.DAYS);
}
}使用布隆過濾器:
查詢redis之前先走布隆過濾器,如果布隆過濾器判斷沒有則一定沒有。
@RestController
@RequestMapping("/seckillOrder")
public class SeckillOrderController {
//如果有多個布隆過濾器,就同時使用@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有誤或此商品不參與秒殺或此商品秒殺已結束");
}else {
Assert.isTrue(false,"未找到該商品,商品ID有誤或此商品不參與秒殺或此商品秒殺已結束");
}
}
}到此這篇關于Java SpringBoot使用guava的布隆過濾器的文章就介紹到這了,更多相關SpringBoot使用guava 內(nèi)容請搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關文章希望大家以后多多支持腳本之家!
相關文章
Jenkins?Pipline實現(xiàn)及原理示例解析
這篇文章主要為大家介紹了Jenkins?Pipline實現(xiàn)及原理示例解析,有需要的朋友可以借鑒參考下,希望能夠有所幫助,祝大家多多進步,早日升職加薪2023-07-07
Java Math類的三個方法ceil,floor,round用法
這篇文章主要介紹了Java Math類的三個方法ceil,floor,round用法,具有很好的參考價值,希望對大家有所幫助。如有錯誤或未考慮完全的地方,望不吝賜教2021-07-07
mybatis.type-aliases-package之巨坑的解決
這篇文章主要介紹了mybatis.type-aliases-package之巨坑的解決,具有很好的參考價值,希望對大家有所幫助。2021-09-09

