布隆过滤器简介
前言:
1,布隆过滤器(BloomFilter):本质概率型的数据结构,以牺牲准确率换取空间和效率的数据结构
2,结构:
二进制数组,一系列Hash函数
3,用途
1.判断一个数,是否存在,如果不存在,就一定不存在
2.判断一个数,是否存在,如果存在,可能存在
3.删除和更新困难CBF(Counting BloomFilter)
4,使用场景
1.垃圾邮箱
2.骚扰电话
3.黑名单
5,实现方式
1.google 的 guava工具包
2.redisson
1,实现方式讲解
1,google的guava
依赖:
<dependency>
<groupId>com.google.guava</groupId>
<artifactId>guava</artifactId>
<version>22.0</version>
</dependency>
/**
* @author psd 布隆过滤器测试
*/
public class GuavaBlooFilterDemo {
public static void main(String[] args) {
// 创建布隆过滤器 大小为20,容错率是0.3
BloomFilter<CharSequence> bloomFilter = BloomFilter.create(Funnels.stringFunnel(Charsets.UTF_8), 20, 0.3);
// 布隆过滤器添加元素
bloomFilter.put("1");
bloomFilter.put("2");
bloomFilter.put("3");
bloomFilter.put("4");
bloomFilter.put("5");
bloomFilter.put("6");
bloomFilter.put("7");
bloomFilter.put("8");
bloomFilter.put("9");
bloomFilter.put("10");
bloomFilter.put("11");
bloomFilter.put("12");
bloomFilter.put("13");
// 判断一个数是否存在
System.out.println(bloomFilter.mightContain("1"));
System.out.println(bloomFilter.mightContain("5"));
System.out.println(bloomFilter.mightContain("6"));
System.out.println(bloomFilter.mightContain("11"));
System.out.println(bloomFilter.mightContain("12"));
System.out.println(bloomFilter.mightContain("13"));
System.out.println(bloomFilter.mightContain("14"));
System.out.println(bloomFilter.mightContain("15"));
System.out.println(bloomFilter.mightContain("16"));
System.out.println(bloomFilter.mightContain("17"));
System.out.println(bloomFilter.mightContain("18"));
System.out.println(bloomFilter.mightContain("19"));
}
}
2,redisson 实现方式
@SpringBootTest
public class RedissonBloomFilterTest {
@Autowired
private RedissonClient redissonClient;
@Test
public void testBloomFilter(){
RBloomFilter<String> bloomFilter = this.redissonClient.getBloomFilter("bloomFilter");
// 插入数量 25 容错率 0.3
bloomFilter.tryInit(25,0.3);
bloomFilter.add("1");
bloomFilter.add("2");
bloomFilter.add("3");
bloomFilter.add("4");
bloomFilter.add("5");
bloomFilter.add("6");
bloomFilter.add("7");
bloomFilter.add("8");
bloomFilter.add("9");
bloomFilter.add("10");
bloomFilter.add("11");
System.out.println(bloomFilter.contains("1"));
System.out.println(bloomFilter.contains("4"));
System.out.println(bloomFilter.contains("7"));
System.out.println(bloomFilter.contains("16"));
System.out.println(bloomFilter.contains("17"));
System.out.println(bloomFilter.contains("18"));
System.out.println(bloomFilter.contains("21"));
System.out.println(bloomFilter.contains("22"));
System.out.println(bloomFilter.contains("23"));
System.out.println(bloomFilter.contains("24"));
System.out.println(bloomFilter.contains("25"));
}
}
2,实战
1,创建布隆过滤器的配置类
package com.xx.xxx.xx.config;
import com.baomidou.mybatisplus.core.toolkit.CollectionUtils;
import org.redisson.api.RBloomFilter;
import org.redisson.api.RedissonClient;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import java.util.List;
/**
* @author psd 布隆过滤器 配置类
*/
@Configuration
public class BloomFilterConfig {
@Autowired
private RedissonClient redissonClient;
@Autowired
private CategoryClient categoryClient;
public static final String KEY_PREFIX = "index:category:[";
@Bean
public RBloomFilter bloomFilter(){
// 创建配置类的名称 大小,容错率
RBloomFilter<Object> bloomFilter = this.redissonClient.getBloomFilter("index:bloom");
bloomFilter.tryInit(1000l,0.03);
ResponseVo<List<CategoryEntity>> listResponseVo = this.categoryClient.queryCategory(0l);
List<CategoryEntity> categoryEntityList = listResponseVo.getData();
if (CollectionUtils.isNotEmpty(categoryEntityList)){
categoryEntityList.forEach(categoryEntity -> {
// 布隆过滤器的添加元素,这里要注意是数组需要用 ]结尾
bloomFilter.add(KEY_PREFIX + categoryEntity.getId() + "]");
});
}
return bloomFilter;
}
}
2,缓存前使用
1.切面类
package com.xxx.xxx.xx.aspect;
import com.alibaba.fastjson.JSON;
import org.apache.commons.lang3.StringUtils;
import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.Signature;
import org.aspectj.lang.annotation.Around;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.reflect.MethodSignature;
import org.redisson.api.RBloomFilter;
import org.redisson.api.RLock;
import org.redisson.api.RedissonClient;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.redis.core.StringRedisTemplate;
import org.springframework.stereotype.Component;
import java.lang.reflect.Method;
import java.util.Arrays;
import java.util.List;
import java.util.Random;
import java.util.concurrent.TimeUnit;
/**
* @author psd 注解的切面类
*/
@Aspect
@Component
public class CategoryCacheAspect {
@Autowired
private StringRedisTemplate redisTemplate;
@Autowired
private RedissonClient redissonClient;
@Autowired
private RBloomFilter bloomFilter;
@Around("@annotation(com.xx.xxx.index.aspect.CategoryCache)")
public Object around(ProceedingJoinPoint joinPoint) throws Throwable {
// 获取方法获取签名
MethodSignature signature = (MethodSignature) joinPoint.getSignature();
// 方法的名字
Method method = signature.getMethod();
// 获取方法上的特定注解
CategoryCache cache = method.getAnnotation(CategoryCache.class);
// gmallcache 的前缀
String prefix = cache.prefix();
// 调用目标方法的参数 组装数据是否存在
List<Object> args = Arrays.asList(joinPoint.getArgs());
String key = prefix + args;
// 查询布隆过滤器 查看是否存在
if (!bloomFilter.contains(key)){
return null;
}
// 查询缓存是否存在
String json = this.redisTemplate.opsForValue().get(key);
if (StringUtils.isNotBlank(json)) {
return JSON.parseObject(json, signature.getReturnType());
}
// 为了防止缓存击穿添加分布式锁
String lockPrefix = cache.lock();
RLock lock = redissonClient.getLock(lockPrefix + args);
lock.lock();
try {
String json2 = this.redisTemplate.opsForValue().get(key);
if (StringUtils.isNotBlank(json2)) {
return JSON.parseObject(json2, signature.getReturnType());
}
// 远程调用或者查询本地数据库 放入缓存
Object result = joinPoint.proceed(joinPoint.getArgs());
// 放入缓存,释放分布式锁
int timeout = cache.timeout() + new Random().nextInt(cache.random());
this.redisTemplate.opsForValue().set(key, JSON.toJSONString(result), timeout, TimeUnit.MINUTES);
return result;
} finally {
lock.unlock();
}
}
}
2.自定义缓存注解
package com.xx.xx.index.aspect;
import java.lang.annotation.*;
/**
* @author psd 作用的范围方法上
*/
@Target({ ElementType.METHOD })
@Retention(RetentionPolicy.RUNTIME)
@Documented
public @interface CategoryCache{
/**
* 缓存过期时间 min 一天
*
* @return 过期时间
*/
int timeout() default 1440;
/**
* 防止缓存雪崩设置随机过期时间
*
* @return min 随机过期时间
*/
int random() default 100;
/**
* 为了防止缓存击穿,添加分布式锁 这里指定锁前缀
*
* @return 锁前缀
*/
String lock() default "lock";
/**
* 缓存的前缀
*
* @return 前缀
*/
String prefix() default "";
}
3,service 方法前使用:
@CategoryCache(prefix = KEY_PREFIX, timeout = 259200, random = 7000, lock = LOCK_PREFIX)
public List<CategoryEntity> queryLvlTwoCategoriesWithSub(Long pid) {
ResponseVo<List<CategoryEntity>> listResponseVo = categoryClient.queryCategoriesWithSub(pid);
List<CategoryEntity> categoryEntityList = listResponseVo.getData();
return categoryEntityList;
}
喜欢我的文章记得点个在看,或者点赞,持续更新中ing…