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中的布隆过滤器。

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

相关推荐
hnlucky44 分钟前
redis 数据类型新手练习系列——Hash类型
数据库·redis·学习·哈希算法
AnsenZhu2 小时前
2025年Redis分片存储性能优化指南
数据库·redis·性能优化·分片
李菠菜4 小时前
非SpringBoot环境下Jedis集群操作Redis实战指南
java·redis
我的golang之路果然有问题4 小时前
快速了解redis,个人笔记
数据库·经验分享·redis·笔记·学习·缓存·内存
道友老李5 小时前
【存储中间件】Redis核心技术与实战(五):Redis缓存使用问题(BigKey、数据倾斜、Redis脑裂、多级缓存)、互联网大厂中的Redis
redis·缓存·中间件
尤物程序猿16 小时前
【2025面试Java常问八股之redis】zset数据结构的实现,跳表和B+树的对比
数据结构·redis·面试
冰^18 小时前
MySQL VS SQL Server:优缺点全解析
数据库·数据仓库·redis·sql·mysql·json·数据库开发
zru_960218 小时前
Docker 部署 Redis:快速搭建高效缓存服务
redis·缓存·docker
axinawang19 小时前
springboot整合redis实现缓存
spring boot·redis·缓存
Spring小子19 小时前
黑马点评商户查询缓存--缓存更新策略
java·数据库·redis·后端