Redis

关注公众号 jb51net

关闭
首页 > 数据库 > Redis > Redis布隆过滤器解决缓存雪崩

Redis使用布隆过滤器解决缓存雪崩的问题

作者:武梓龙_Wzill

布隆过滤器可以帮助我们解决Redis缓存雪崩的问题,那什么是布隆过滤器、布隆过滤器又是如何使用如何解决缓存雪崩的问题的,让我们带着这一系列的问题去详细了解布隆过滤器,感兴趣的小伙伴跟着小编一起来看看吧

背景介绍

布隆过滤器可以帮助我们解决Redis缓存雪崩的问题,那什么是布隆过滤器、布隆过滤器又是如何使用如何解决缓存雪崩的问题的,让我们带着这一系列的问题去详细了解布隆过滤器。

概念说明

布隆过滤器是一种用于快速判断一个元素是否属于一个集合的数据结构。它通常用于大规模数据集合中,可以快速判断一个元素是否可能存在于集合中,但不能确定一定存在。布隆过滤器的主要优点是占用内存少、查询速度快,并且可以容忍一定的误判率。

原理说明

布隆过滤器由一个位数组和多个哈希函数组成。位数组通常初始化为0,哈希函数用于将元素映射到位数组中的多个位置。当一个元素被加入到布隆过滤器中时,它会被哈希函数映射到位数组的多个位置,然后将这些位置的值设为1。当查询一个元素是否存在于布隆过滤器中时,哈希函数会将元素映射到位数组的多个位置,然后检查这些位置的值是否都为1,如果有一个位置的值为0,则可以确定元素一定不存在于集合中,如果所有位置的值都为1,则元素可能存在于集合中。

布隆过滤器的误判率取决于位数组的大小和哈希函数的数量。通常情况下,误判率随着位数组大小的增加而减小,但会占用更多的内存。因此,使用布隆过滤器时需要根据实际情况权衡误判率和内存占用。

在这里插入图片描述

解决穿透

我们还可以在存储和缓存之前,加⼀个布隆过滤器,做⼀层过滤。布隆过滤器⾥会保存数据是否存在,如果判断数据不存在,就不会访问存储。

在这里插入图片描述

安装使用

安装过程

Redis为普通安装的配置方式

1、下载布隆过滤器这个插件

wget https://github.com/RedisLabsModules/rebloom/archive/v2.2.6.tar.gz

2、解压文件

tar -zxvf v2.2.6.tar.gz

在这里插入图片描述

3、编辑插件

# 到RedisBloom对应目录
cd /usr/local/redis/RedisBloom-2.2.6
# 编译插件
make

4、Redis集成RedisBloom插件

# vim查看redis.conf
vim /usr/local/redis/config/redis.conf
# 在文件后面添加如下配置
loadmodule /usr/local/redis/RedisBloom-2.2.6/redisbloom.so

在这里插入图片描述

5、配置完之后重启Redis即可。

Redis为Docker镜像安装的配置方式

1、创建文件夹以及配置文件,用于挂在redis启动的后容器中的文件,方便我们在容器外部操作redis的配置

mkdir data  ##创建文件夹
touch redis.conf  ## 创建文件

2、在我们创建的redis.conf文件中添加一行配置loadmodule /data/RedisBloom-2.2.6/redisbloom.so

在这里插入图片描述

3、随后直接使用dokcer run命令进行启动

docker run -p 6379:6379 --name redis -v /root/redis/data:/data -v 
/root/redis/redis.conf:/etc/redis/redis.conf --restart=always 
--network host  -d redis:5.0.7 redis-server /etc/redis/redis.conf

这个命令是用于在 Docker 中运行 Redis 容器,并进行一些配置。下面是对每个参数的解释:

执行上述操作redis容器如果启动没有问题那么我们的布隆过滤器的插件和redis都安装并启动成功了,如果没有启动成功可以通过docker logs 查看一下redis的启动过程中出现什么问题。

具体使用

控制台操作命令说明

BF.ADD <key> <item>
BF.EXISTS <key> <item>
BF.MADD <key> <item> [item ...]
BF.MEXISTS <key> <item> [item ...]
BF.INFO <key>
BF.RESERVE <key> <error_rate> <capacity>
BF.COUNT <key>

给user过滤器添加一个元素,如果我们没有添加创建布隆过滤器,系统会给我们创建一个,其中布隆过滤器的容量为100,判错率为0.01这是布隆过滤器的默认配置,我们可以在创建布隆过滤器的时候进行修改。

在这里插入图片描述

Spring Boot集成布隆过滤器

1、引入依赖:这里使用的redis的过滤器所以用到的依赖直接使用的spring-data-redis这个就可以了。

        <!--redis的依赖-->
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-data-redis</artifactId>
        </dependency>

2、布隆过滤器的工具类

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.data.redis.core.script.DefaultRedisScript;
import org.springframework.data.redis.core.script.RedisScript;
import org.springframework.data.redis.serializer.RedisSerializer;
import org.springframework.stereotype.Component;
import org.springframework.transaction.annotation.Transactional;

import java.util.Collections;
import java.util.List;
import java.util.stream.Collectors;

@Component
public class RedisBloomUtil {
    @Autowired
    private RedisTemplate redisTemplate;
    // 初始化一个布隆过滤器
    public Boolean tryInitBloomFilter(String key, long expectedInsertions, double falseProbability) {
        Boolean keyExist = redisTemplate.hasKey(key);
        if(keyExist) {
            return false;
        }
        RedisScript<Boolean> script = new DefaultRedisScript<>(bloomInitLua(), Boolean.class);
        RedisSerializer stringSerializer = redisTemplate.getStringSerializer();
        redisTemplate.execute(script, stringSerializer, stringSerializer, Collections.singletonList(key), falseProbability+"", expectedInsertions+"");
        return true;
    }
    // 添加元素
    public Boolean addInBloomFilter(String key, Object arg) {
        RedisScript<Boolean> script = new DefaultRedisScript<>(addInBloomLua(), Boolean.class);
        return (Boolean) redisTemplate.execute(script, Collections.singletonList(key), arg);
    }
    @Transactional
    // 批量添加元素
    public Boolean batchAddInBloomFilter(String key, Object... args) {
        RedisScript<Boolean> script = new DefaultRedisScript<>(batchAddInBloomLua(), Boolean.class);
        return (Boolean) redisTemplate.execute(script, Collections.singletonList(key), args);
    }
    // 查看某个元素是否是存在
    public Boolean existInBloomFilter(String key, Object arg) {
        RedisScript<Boolean> script = new DefaultRedisScript<>(existInBloomLua(), Boolean.class);
        return (Boolean) redisTemplate.execute(script, Collections.singletonList(key), arg);
    }
    // 批量查看元素是否存在
    public List batchExistInBloomFilter(String key, Object... args) {
        RedisScript<List> script = new DefaultRedisScript(batchExistInBloomLua(), List.class);
        List<Long> results = (List) redisTemplate.execute(script, Collections.singletonList(key), args);
        List<Boolean> booleanList = results.stream().map(res -> res == 1 ? true : false).collect(Collectors.toList());
        return booleanList;
    }


    private String bloomInitLua() {
        return "redis.call('bf.reserve', KEYS[1], ARGV[1], ARGV[2])";
    }
    private String addInBloomLua() {
        return "return redis.call('bf.add', KEYS[1], ARGV[1])";
    }
    private String batchAddInBloomLua() {
        StringBuilder sb = new StringBuilder();
        sb.append("for index, arg in pairs(ARGV)").append("\r\n");
        sb.append("do").append("\r\n");
        sb.append("redis.call('bf.add', KEYS[1], arg)").append("\r\n");
        sb.append("end").append("\r\n");
        sb.append("return true");
        return sb.toString();
    }
    private String existInBloomLua() {
        return "return redis.call('bf.exists', KEYS[1], ARGV[1])";
    }
    private String batchExistInBloomLua() {
        StringBuilder sb = new StringBuilder();
        sb.append("local results = {}").append("\r\n");
        sb.append("for index, arg in pairs(ARGV)").append("\r\n");
        sb.append("do").append("\r\n");
        sb.append("local exist = redis.call('bf.exists', KEYS[1], arg)").append("\r\n");
        sb.append("table.insert(results, exist)").append("\r\n");
        sb.append("end").append("\r\n");
        sb.append("return results;");
        return sb.toString();
    }
}

总结提升

布隆过滤器适用于需要快速判断一个元素是否可能存在于集合中的场景,例如网络爬虫中的去重、缓存中的数据判断等。但需要注意的是,布隆过滤器无法删除元素,也无法准确地判断一个元素是否存在于集合中,因此在一些场景下可能会产生误判。

以上就是Redis使用布隆过滤器解决缓存雪崩的问题的详细内容,更多关于Redis布隆过滤器解决缓存雪崩的资料请关注脚本之家其它相关文章!

您可能感兴趣的文章:
阅读全文