79.Redis基于布隆过滤器解决缓存穿透问题

一、缓存穿透常用解决方案有那些

在这里插入图片描述
减轻访问数据库压力。

缓存穿透的问题?

缓存的穿透:指定使用一些不存在的key进行大量的查询Redis,导致无法命中
每次的请求都会要传查询到我们数据库;对我们的数据库的压力非常大;

解决方法:

1.对我们的服务接口api实现限流用户授权黑名单和白名单拦截;(最核心)
2.从缓存和数据库都查询不到结果的话,一样将数据库空值结果缓存到Redis
中;设置30s的有效期避免使用同一个id对数据库攻击。
如果黑客真的在攻击的情况下,随机成id肯定是不一样的。
3.布隆过滤器

在这里插入图片描述
在这里插入图片描述
在这里插入图片描述

二、布隆过滤器简单的介绍

1、什么是布隆过滤器呢?

HashMap? 检测该key是否存在 原理:先计算对应的key存放到数组的Index? O(1)(在hash值没有产生冲突情况下)

2、布隆过滤器 作用:

布隆过滤器适用于判断一个元素在集合中是否存在,但是可能会存在误判的问题。概率极低
实现的原理采用二进制向量数组随机映射hash函数

三、布隆过滤器实现原理的分析

在这里插入图片描述
在这里插入图片描述
在这里插入图片描述

3.布隆过滤器为什么会产生冲突

会根据key计算hash值,可能与布隆过滤器中存放的元素hash产生冲突都是为1,布隆可能会产生误判可能存在。
比如:
mayiket: 可以拆分成3个字节 ,分别对应的hash值是:

ma yi ket
4 10 13

yushengjun可以拆分成3个字节 ,分别对应的hash值是:

yush eng jun
13 16 19

这个时候来了 一个吗 meit。也可以拆分成3个字节 ,分别对应的hash值是:
me = ma 的哈希值 4
i= eng 的哈希值是 16
t = ket的哈希值是13
3个字节的hash值都找到了,但是 他只是刚刚好和已经存在 的 字节相同。本身并不存在。但是会误判为 存在。

如何解决这个问题:

二进制数组长度设置比较大,可以减少布隆误判的概率。

扫描二维码关注公众号,回复: 8815117 查看本文章

四、java语言使用布隆过滤器使用

在这里插入图片描述

import com.google.common.hash.BloomFilter;
import com.google.common.hash.Funnels;

import java.util.ArrayList;
public class BlongTest {

    /**
     * 假设集合中存放100万条数据
     */
    private static Integer size = 1000000;

    public static void main(String[] args) {
        BloomFilter<Integer> integerBloomFilter = BloomFilter.create(Funnels.integerFunnel(), size, 0.0000000001);
        for (int i = 0; i < size; i++) {
            // 向我们布隆过滤器中存放100万条数据
            integerBloomFilter.put(i);
        }
        ArrayList<Integer> integers = new ArrayList<>();
        for (int j = size; j < size + 10000; j++) {
            // 使用该pai判断key在布隆过滤器中是否存在 返回true 存在 false  表示不存在
            if (integerBloomFilter.mightContain(j)) {
                // 将布隆过滤器误判的结果存放到集合中方便后期统计
                integers.add(j);
            }
        }
        System.out.println("布隆过滤器误判的结果:" + integers.size());
        // 0.03概率 数组长度730万左右 0.01
        // 0.01概率 数组长度960万左右
    }
}

在这里插入图片描述
在这里插入图片描述
在这里插入图片描述

误判概率不能是0

在这里插入图片描述
在这里插入图片描述

五、布隆过滤器如何减少误判的概率

在这里插入图片描述
源码误判概率是:3%

在这里插入图片描述

六、使用布隆过滤器解决Redis穿透问题

@RequestMapping("/dbToBulong")
    public String dbToBulong() {
        // 1.从数据库预热id到布隆过滤器中
        List<Integer> orderIds = orderMapper.getOrderIds();
        integerBloomFilter = BloomFilter.create(Funnels.integerFunnel(), orderIds.size(), 0.01);
        for (int i = 0; i < orderIds.size(); i++) {
            // 添加到我们的布隆过滤器中
            integerBloomFilter.put(orderIds.get(i));
        }

        return "success";
    }
@RequestMapping("/getOrder")
    public OrderEntity getOrder(Integer orderId) {

        // 0.判断我们的布隆过滤器
        if (!integerBloomFilter.mightContain(orderId)) {
            System.out.println("从布隆过滤器中查询不存在");
            return null;
        }

        // 1.先查询Redis中数据是否存在
        OrderEntity orderRedisEntity = (OrderEntity) redisTemplateUtils.getObject(orderId + "");
        if (orderRedisEntity != null) {
            System.out.println("直接从Redis中返回数据");
            return orderRedisEntity;
        }
        // 2. 查询数据库的内容
        System.out.println("从DB查询数据");
        OrderEntity orderDBEntity = orderMapper.getOrderById(orderId);
        if (orderDBEntity != null) {
            System.out.println("将Db数据放入到Redis中");
            redisTemplateUtils.setObject(orderId + "", orderDBEntity);
        }
        return orderDBEntity;
    }

项目要求不严谨的时候,可以用 布隆过滤器。

1.从数据库预热id到布隆过滤器中 , 添加到我们的布隆过滤器中

2.判断我们的布隆过滤器

3.先查询Redis中数据是否存在

4. 查询数据库的内容

 @RequestMapping("/addOrder")
    public String addOrder() {
        // 1.提前生成订单token 临时且唯一
        String orderToken = UUID.randomUUID().toString();
        Long orderId = System.currentTimeMillis();
        // 2.将我们的token存放到rdis中
        redisUtils.setString(orderToken, orderId + "", 10L);
        OrderEntity orderEntity = new OrderEntity(null, "永久", orderId + "", orderToken);
        return orderMapper.insertOrder(orderEntity) > 0 ? "success" : "fail";
    }

redis工具类

@Component
public class RedisTemplateUtils {

    @Resource
    private RedisTemplate<String, Object> redisTemplate;

    public void setObject(String key, Object value) {
        setObject(key, value, null);
    }

    public void setObject(String key, Object value, Long timeOut) {
        redisTemplate.opsForValue().set(key, value);
        if (timeOut != null) {
            redisTemplate.expire(key, timeOut, TimeUnit.SECONDS);
        }
    }


    public Object getObject(String key) {
        return redisTemplate.opsForValue().get(key);
    }
}
@Component
public class RedisUtils {

    /**
     * 获取我们的redis模版
     */
    @Autowired
    private StringRedisTemplate stringRedisTemplate;


    public void setString(String key, String value) {
        setString(key, value, null);
    }

    public void setString(String key, String value, Long timeOut) {
        stringRedisTemplate.opsForValue().set(key, value);
        if (timeOut != null) {
            stringRedisTemplate.expire(key, timeOut, TimeUnit.SECONDS);
        }
    }


    public String getString(String key) {
        return stringRedisTemplate.opsForValue().get(key);
    }
    /**
     * redis当成数据库中
     *
     * 注意事项:对我们的redis的key设置一个有效期
     */


}
发布了119 篇原创文章 · 获赞 12 · 访问量 2万+

猜你喜欢

转载自blog.csdn.net/weixin_44722978/article/details/103484246