Redis基于布隆过滤器解决缓存穿透问题(15)

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

1.布隆过滤器基本介绍

布隆过滤器适用于判断某个数据是否在集合中存在,可能存在一定的误判, Bloom Filter基本实现原理采用位数组与联合函数一起实现;实现的原理采用二进制向量数组和随机映射hash函数。

布隆过滤器为什么会产生冲突 ,会根据key计算hash值,可能与布隆过滤器中存放的元素hash产生冲突都是为1,布隆可能会产生误判可能存在。

如何解决这个问题,可以将二进制数组长度设置比较大,可以减少布隆误判的概率。
【适合的场景】

  1. 防止缓存穿透直接访问数据库
  2. 判断用户是否阅读过某一个视频或文章;类似抖音,快手,可能误判,但是不会看到重复的内容
  3. 做黑名单过滤,针对不同的用户是否存在白名单和黑名单,可能误判,但一定程度可以解决问题等等

2.布隆过滤器的优缺点

优点

  1. 支持海量数据场景下高效的判断元素是否存在

  2. 空间占用极小,因为本身不存储数据而是用比特位表示数据是否存在,某种程度有保密的效果。

缺点

  1. 不存储数据本身,所以只能添加但不可以删除,因为删除会导致误判率增加

  2. 由于存在hash碰撞,不一定百分百判断准确,存在一定误差

3.布隆过滤器的原理

布隆过滤器的原理是,当一个元素被加入集合时,通过 K 个散列函数将这个元素映射成一个位数组中的 K 个点(offset),把它们置为 1。检索时,我们只要看看这些点是不是都是 1 就(大约)知道集合中有没有它了:如果这些点有任何一个 0,则被检元素一定不在;如果都是 1,则被检元素很可能在。这就是布隆过滤器的基本思想。

简单来说就是准备一个长度为 m 的位数组并初始化所有元素为 0,用 k 个散列函数对元素进行 k 次散列运算跟 len(m)取余得到 k 个位置并将 m 中对应位置设置为 1。

4.缓存穿透问题

缓存穿透是指使用不存在的key进行大量的高并发查询,导致缓存无法命中,每次请求都要都要穿透到后端数据库查询,使得数据库的压力非常大,甚至导致数据库服务卡死;

应对的方案:

  1. 对我们的服务接口api实现限流、用户授权、黑名单和白名单拦截;
  2. 从缓存和数据库都查询不到结果的话,将数据库空值结果缓存到Redis中;设置合理过期时间(如:5s)避免使用同一个id对数据库攻击。
    如果黑客真的在攻击的情况下,随机成id肯定是不一样的,可采用布隆过滤器。
  3. 布隆过滤器
xml 复制代码
<dependency>
    <groupId>com.google.guava</groupId>
    <artifactId>guava</artifactId>
    <version>32.1.2-jre</version>
</dependency>
java 复制代码
public class BlongTest {
    /**
     * 在布隆中存放100万条数据
     */
    private static Integer size = 1000000;

    public static void main(String[] args) {
        BloomFilter<Integer> integerBloomFilter = BloomFilter.create(Funnels.integerFunnel(), size, 0.01);
        for (int i = 0; i < size; i++) {
            integerBloomFilter.put(i);
        }
        // 从布隆中查询数据是否存在
        ArrayList<Integer> strings = new ArrayList<>();
        for (int j = size; j < size + 10000; j++) {
            if (integerBloomFilter.mightContain(j)) {
                strings.add(j);
            }
        }
        System.out.println("误判数量:" + strings.size());
    }
}

5.解决Redis缓存穿透问题

java 复制代码
public static BloomFilter<Integer> integerBloomFilter = null;

@RequestMapping("/getOrder")
public OrderEntity getOrder(Integer orderId) {
    if (integerBloomFilter != null) {
        if (!integerBloomFilter.mightContain(orderId)) {
            System.out.println("从布隆过滤器中检测到该key不存在");
            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;
}

@RequestMapping("/dbToBulong")
public String dbToBulong() {
    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";
}

计算布隆过滤器在线网址:布隆过滤计算器

Guava 提供的布隆过滤器的实现还是很不错的,但是它有一个重大的缺陷就是只能单机使用,而现在互联网一般都是分布式的场景。为了解决这个问题就需要用到Redis中的布隆过滤器。

参考:详解布隆过滤器的原理和实现

相关推荐
十六ᵛᵃᵉ26 分钟前
day6_FlinkSQL实战
java·redis·ajax
嘉友27 分钟前
Redis zset数据结构以及时间复杂度总结(源码)
数据结构·数据库·redis·后端
hweiyu001 小时前
从JVM到分布式锁:高并发架构设计的六把密钥
jvm·redis·分布式·mysql·etcd
萱萱1995044 小时前
Spring Boot实战:MySQL与Redis数据一致性深度解析与代码实战
spring boot·redis·mysql
ashane13144 小时前
Redis的一些高级指令
redis·git·bootstrap
天上掉下来个程小白6 小时前
Redis-12.在Java中操作Redis-Spring Data Redis使用方式-操作字符串类型的数据
java·redis·spring·springboot·苍穹外卖
Amd79411 小时前
FastAPI中Pydantic异步分布式唯一性校验
redis·fastapi·分布式锁·多级缓存·pydantic·唯一性校验·异步校验
J不A秃V头A14 小时前
Redis批量操作详解
开发语言·redis
Y第五个季节17 小时前
Redis - 概述
redis