布隆过滤器简介

布隆过滤器简介

前言:

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…

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值