您好,欢迎来到刀刀网。
搜索
您的当前位置:首页【Redis实战篇】利用布隆过滤器解决缓存穿透问题

【Redis实战篇】利用布隆过滤器解决缓存穿透问题

来源:刀刀网

前言

什么是缓存穿透?

查询一个不存在的数据,mysql查不到数据也会不直接写入缓存,就会导致每次请求都查询数据库

海量用户如果说查询的用户名存在或不存在,全部请求数据库,会将数据库直接打满。

解决方案

1.将DB所有数据放入缓存中

将数据库所有的数据放入缓存中。

存入缓存引出的问题
这里的数据都是永久数据。占用Reids内存太高。所以不采取这个方案。

2.布隆过滤器

下面为流程图

什么是布隆过滤器

位图

所谓位图,就是用每一位来存放某种状态,适用于海量数据,整数,数据无重复的场景。通常是用来判断某个数据存不存在的。

布隆过滤器概念

布隆过滤器是由布隆(Burton Howard Bloom)在1970年提出的 一种紧凑型的、比较巧妙的概率型数据结构,特点是高效地插入和查询,可以用来告诉你 “某样东西一定不存在或者可能存在”,它是用多个哈希函数,将一个数据映射到位图结构中。此种方式不仅可以提升查询效率,也可以节省大量的内存空间

优点: 内存占用少、没有多余key
缺点: 实现复杂,存在误判

布隆过滤器的误判

首先布隆过滤器的误判是可以调整的。

  • 布隆过滤器要设置初始容量。容量设置越大,冲突几率越低。
  • 布隆过滤器会设置预期的误判值。

其次在使用布隆过滤器时,就需要考虑误判是不是可以接受

例如在用户设置用户名时,“aaaa”在数据库中不存在,但是在布隆过滤器中的结果返回是存在的,那么“aaaa”这个用户名就不能再被注册,这对用户并不会造成实质性的损失,所以这个误判就是可以接受的。

而在有金钱交易的业务场景中,误判就是不能被接受的。

实际应用

1.引入依赖

1.1引入Redisson依赖

<dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-data-redis</artifactId>
</dependency>

<dependency>
    <groupId>org.redisson</groupId>
    <artifactId>redisson-spring-boot-starter</artifactId>
</dependency>

1.2 配置Redis参数

2.创建布隆过滤器实例

import org.redisson.api.RBloomFilter;
import org.redisson.api.RedissonClient;
import org.springframework.boot.context.properties.EnableConfigurationProperties;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;

/**
 * 布隆过滤器配置
 */
@Configuration
public class RBloomFilterConfiguration {

    
    @Bean
    public RBloomFilter<String> CachePenetrationBloomFilter(RedissonClient redissonClient) {
        RBloomFilter<String> cachePenetrationBloomFilter = redissonClient.getBloomFilter("xxx");
        cachePenetrationBloomFilter.tryInit(0, 0);
        return cachePenetrationBloomFilter;
    }
}

这里tryInit有两个核心参数

public boolean tryInit(long expectedInsertions, double falseProbability)

误判率越低,位数组越长,布隆过滤器占用内存越大;
误判率越低,散列Hash函数越多,计算耗时越长。

3.在代码中使用

private final RBloomFilter<String> userRegisterCachePenetrationBloomFilter;

4. 应用中存在问题

当布隆过滤器中不存在时,代表数据要插入数据库,如果恶意短时间内(毫秒级)发送海量请求,这些请求都要落到数据库,造成数据库压力,此时就要引入分布式锁来解决问题,锁定当前数据进行串行执行,防止恶意请求将数据打到数据库。

 RLock lock = redissonClient.getLock(...);
       try {
           if (lock.tryLock()){
               int insert = baseMapper.insert(...);
               if (insert < 1){
                   throw new ClientException(...);
               }
               userRegisterCachePenetrationBloomFilter.add(...);
               return;
           }
           throw new ClientException(...);
       }finally {
           lock.unlock();
       }

因篇幅问题不能全部显示,请点此查看更多更全内容

Copyright © 2019- gamedaodao.com 版权所有 湘ICP备2022005869号-6

违法及侵权请联系:TEL:199 18 7713 E-MAIL:2724546146@qq.com

本站由北京市万商天勤律师事务所王兴未律师提供法律服务