Redis中如何使用布隆过滤器解决缓存击穿问题

 当使用 SpringBoot 结合 Redis 缓存时,我们需要考虑如何防止 Redis 缓存穿透。虽然缓存可以显著提高应用程序的性能,但如果缓存穿透发生,就可能导致数据不一致和性能下降。本文将介绍如何在 SpringBoot 中使用布隆过滤器预防 Redis 缓存穿透问题,并演示代码。

一、Redis三大经典问题

缓存击穿

缓存穿透说简单点就是大量请求的 key 是不合理的,根本不存在于缓存中,也不存在于数据库中 。这就导致这些请求直接到了数据库上,根本没有经过缓存这一层,对数据库造成了巨大的压力,可能直接就被这么多请求弄宕机了。

缓存穿透

缓存击穿中,请求的 key 对应的是 热点数据 ,该数据 存在于数据库中,但不存在于缓存中(通常是因为缓存中的那份数据已经过期) 。这就可能会导致瞬时大量的请求直接打到了数据库上,对数据库造成了巨大的压力,可能直接就被这么多请求弄宕机了。

缓存雪崩

缓存中大量key同时过期,导致请求大部分打到数据库里。

二、解决办法

缓存击穿:

  • 缓存无效key
  • 布隆过滤器

缓存雪崩:

  • 设置不同的失效时间比如随机设置缓存的失效时间。
  • 缓存永不失效(不太推荐,实用性太差)。
  • 设置二级缓存。

缓存穿透:

  • 设置热点数据永不过期或者过期时间比较长。
  • 针对热点数据提前预热,将其存入缓存中并设置合理的过期时间比如秒杀场景下的数据在秒杀结束之前不过期。
  • 请求数据库写数据到缓存之前,先获取互斥锁,保证只有一个请求会落到数据库上

三、布隆过滤器原理

Redis 中的布隆过滤器 (Blonk 过滤器) 是一种基于位图的过滤机制,用于筛选出满足特定条件的记录。它的原理可以概括为以下几个步骤:

  1. 创建一个布隆过滤器,并将其与一个整数数组相关联。整数数组中的每一行代表一个位图的索引,即哪些位是 1,哪些位是 0。
  2. 对于需要过滤的记录,将其对应的布隆过滤器的索引转换为对应的整数数组索引。
  3. 对整数数组进行位运算,将符合条件的记录筛选出去。具体来说,对于每一行,如果对应的位为 1,则将该记录从结果中删除。
  4. 返回结果。

布隆过滤器的实现原理可以用以下公式表示:

result = ceil(n / log2(width)) * hashval + index

其中,n 是记录的数量,width 是位图的位数,log2 是取模运算符,ceil 是向上取整运算符,result 是结果索引,index 是记录的布隆过滤器索引,hashval 是记录的哈希值。

布隆过滤器的优点在于其高效性和压缩性。它可以快速筛选出符合条件的记录,并且只需要存储符合条件的记录。由于布隆过滤器是 基于位图的,因此它也可以很好地适应大数据量、高并发的场景。

四、实现步骤

  1. 在 SpringBoot 应用程序中添加布隆过滤器依赖。
<dependency>  
    <groupId>org.springframework.boot</groupId>  
    <artifactId>spring-boot-starter-redis</artifactId>  
</dependency>  
复制代码
  1. 创建 Redis 连接。
@Autowired  
private RedisTemplate<String, Object> redisTemplate;

public void setBulFilter(String key, Object value) {  
    RedisConnectionFactory factory = new RedisConnectionFactory();  
    factory.setHost("localhost");  
    factory.setPort(6379);  
    factory.setPassword("password");  
    RedisClient redisClient = factory.build();  
    RedisReply reply = redisClient.set(key, value);  
    System.out.println(reply);  
}
复制代码
  1. 在需要更新缓存的代码处添加触发布隆过滤器的代码。
setBulFilter("key", "newValue");  
复制代码
import java.util.*;

import org.blondel.Blondel;    
import org.blondel.HashFunction;    
import org.blondel.CompareFunction;

public class BlondelCache {    
    // 缓存大小,默认为 10 个缓存项  
    private static final int CUTOFF_POINT = 10;  
    // 缓存项最大长度,默认为 256 个字符  
    private static final int PRIME = 31;  
    // 缓存名称,默认为 white_list 和 black_list  
    private static final String WHITE_LIST = "white_list";  
    private static final String BLACK_LIST = "black_list";

    // 缓存对象,用于存储缓存项和响应  
    private Map<String, String> cache = new HashMap<>();  
    // 布隆过滤器,用于哈希缓存项并存储在缓存中  
    private Blondel blondel = new Blondel();

    public BlondelCache() {  
        // 初始化缓存  
        cache.put(WHITE_LIST, "white_list");  
        cache.put(BLACK_LIST, "black_list");  
    }

    // 将响应添加到缓存中  
    public void setResponse(String cacheKey, String response) {  
        String cacheValue = cache.get(cacheKey);  
        if (cacheValue == null) {  
            // 如果缓存不存在,则创建一个新的缓存  
            cache.put(cacheKey, cacheValue);  
        } else if (cacheValue.equals(response)) {  
            // 如果缓存内容和响应相同,则无需更新缓存  
        } else {  
            // 如果缓存内容和响应不同,则更新缓存  
            cacheValue = response;  
        }  
        blondel.addResponse(response);  
    }

    // 从缓存中获取响应  
    public String getResponse(String cacheKey, String referrer) {  
        String cacheValue = cache.get(cacheKey);  
        if (cacheValue == null) {  
            // 如果缓存不存在,则直接返回响应  
            return referrer != null ? referrer : "";  
        }  
        // 将请求哈希,并根据 white_list 和 black_list 的规则进行缓存  
        String hashValue = blondel.hash(cacheKey.concat(referrer), HashFunction.SHA_256);  
        int index = hashValue.indexOf("-");  
        if (index == -1) {  
            // 如果哈希值中没有 "-" 符号,则将其添加到 white_list 中  
            cacheValue = cacheValue.concat("-");  
            cache.put(WHITE_LIST, cacheValue);  
        } else {  
            // 如果哈希值中有 "-" 符号,则将其添加到 black_list 中  
            cacheValue = cacheValue.concat("-");  
            cache.put(BLACK_LIST, cacheValue);  
        }  
        return cacheValue;  
    }

    public static void main(String[] args) {  
        // 创建缓存对象  
        BlondelCache cache = new BlondelCache();

        // 模拟请求  
        String request = "GET /index.html HTTP/1.1\r\nHost: example.com\r\nConnection: close\r\n\r\n";  
        String response = "HTTP/1.1 200 OK\r\nContent-Type: text/html\r\nDate: Sun, 15 Feb 2023 15:14:15 GMT\r\nConnection: close\r\n\r\n<html><body><h1>Hello World!</h1></body></html>\r\n";

        // 添加缓存  
        cache.setResponse(BLACK_LIST, response);  
        cache.setResponse(WHITE_LIST, request);

        // 获取缓存响应  
        String cacheKey = "black_list:/index.html";  
        String cacheValue = cache.getResponse(cacheKey, request);  
        System.out.println(cacheValue);

        // 更新缓存响应  
        cache.setResponse(BLACK_LIST, response);  
        cache.setResponse(WHITE_LIST, request);  
        cacheValue = cache.getResponse(cacheKey, request);  
        System.out.println(cacheValue);  
    }  
}

复制代码

五、总结

使用布隆过滤器可以有效地预防 Redis 缓存穿透,提高应用程序的性能和稳定性。同时,使用 Redis 的事务也可以提高应用程序的可靠性和稳定性。在实际应用中,可以根据具体情况选择合适的缓存策略,以提高应用程序的性能和稳定性。

  • 2
    点赞
  • 5
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
布隆过滤器是一种非常实用的解决缓存穿透缓存击穿缓存雪崩问题的工具。对于缓存穿透问题布隆过滤器可以在缓存存储空值,避免频繁查询数据库布隆过滤器的原理是通过多次哈希运算将元素映射到一个二进制数组,如果某个位置的值为1,则表示该元素可能存在;如果为0,则表示该元素一定不存在。通过布隆过滤器,可以快速判断一个请求是否需要查询数据库,从而避免了缓存穿透问题。\[3\] 对于缓存击穿问题布隆过滤器可以用于限流和降级策略。通过对热点参数进行限流,可以控制请求的并发量,避免数据库被大量请求压垮。同时,对于无效的请求,可以进行服务降级,直接返回默认值或错误信息,而不是查询数据库。\[2\] 对于缓存雪崩问题布隆过滤器可以作为一种多级缓存解决方案之一。除了使用Redis作为缓存外,还可以使用Nginx缓存等其他缓存工具,将请求分散到不同的缓存层,从而减轻数据库的访问压力。同时,可以通过设置缓存的过期时间,避免大量缓存同时过期,导致数据库访问压力过大。\[2\] 总之,布隆过滤器是一种非常实用的工具,可以有效解决Redis缓存雪崩、缓存穿透缓存击穿问题。通过合理使用布隆过滤器,可以提高系统的性能和稳定性。 #### 引用[.reference_title] - *1* [redis缓存穿透之终极解决方案——布隆过滤器](https://blog.csdn.net/qq_40606397/article/details/114085367)[target="_blank" data-report-click={"spm":"1018.2226.3001.9630","extra":{"utm_source":"vip_chatgpt_common_search_pc_result","utm_medium":"distribute.pc_search_result.none-task-cask-2~all~insert_cask~default-1-null.142^v91^insertT0,239^v3^insert_chatgpt"}} ] [.reference_item] - *2* *3* [redis缓存雪崩、击穿、穿透](https://blog.csdn.net/weixin_45414913/article/details/124901909)[target="_blank" data-report-click={"spm":"1018.2226.3001.9630","extra":{"utm_source":"vip_chatgpt_common_search_pc_result","utm_medium":"distribute.pc_search_result.none-task-cask-2~all~insert_cask~default-1-null.142^v91^insertT0,239^v3^insert_chatgpt"}} ] [.reference_item] [ .reference_list ]
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值