RedisBloom使用

确定使用的RedisBloom模块版本,根据redis版本确定

https://redis.io/docs/latest/operate/oss_and_stack/stack-with-enterprise/release-notes/redisbloom/
在这里插入图片描述

安装RedisBloom模块,从git获取对应的原码,make生成.so文件,挂载.so文件,启动redis

一、解压编译生成.so文件
1.1、下载对应的版本

https://github.com/RedisBloom/RedisBloom
在这里插入图片描述

1.2、#解压
unzip RedisBloom-2.2.18.zip
1.3、#进入bloom目录
cd RedisBloom-2.2.18
1.4、#编译原码生成.so文件
make

在这里插入图片描述

二、docker容器安装
2.1、新建目录/iothub/test-redis/modules,把redisbloom.so文件复制到这个目录下
2.2、赋权
chmod +r /iothub/test-redis/modules/redisbloom.so
2.3、docker启动
docker run --name test-redis -v /iothub/test-redis/data:/data -v /iothub/test-redis/modules:/modules -p 6378:6379 -d redis:4.0.10 redis-server --requirepass jimi@123 --appendonly yes --loadmodule /modules/redisbloom.so
三、容器外安装
3.1、下载redis(已经存在不用)
wget http://download.redis.io/releases/redis-4.0.10.tar.gz
3.2、#进入redis文件夹下面
tar -zxvf redis-4.0.10.tar.gz
#真实redis路径
cd /paas/redis-4.0.10
3.3、#编辑redis.conf文件 redis启动指定的配置文件

在这里插入图片描述

loadmodule /paas/redis-4.0.10/RedisBloom-2.2.18/redisbloom.so

在这里插入图片描述
bind IP设置为0.0.0.0,都可以连接
在这里插入图片描述
设置密码
还可以设置文件挂载路径…

3.4指定配置文件后台启动
./src/redis-server ./redis.conf --daemonize yes
package com.jimi.rtvos.helper;

import com.jimi.rtvos.consts.Constants;
import com.khan.utils.JacksonUtils;
import com.khan.utils.StringUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.stereotype.Component;
import redis.clients.jedis.JedisPool;

import javax.annotation.PostConstruct;
import javax.annotation.Resource;
import java.util.Arrays;
import java.util.List;

/**
 * Description:
 * <p>
 * Redis布隆过滤器
 * </p>
 * <p>Redis 布隆过滤器需要 先安装RedisBloom模块</p>
 * <p>启动服务,需要先创建成功对应的Bloom过滤器,防止使用功能异常</p>
 *
 * @Author: leo.xiong
 * @CreateDate: 2025/8/7 13:52
 * @Email: xionglang@xxxx.com
 * @Since:
 */
@Component
public class RedisBloomHelper {
    private static final Logger LOGGER = LoggerFactory.getLogger(RedisBloomHelper.class);
    private static final String OK = "OK";
    private static final Long SUCCESS = 1L;
    @Resource
    protected JedisPool jedisPool;

    /**
     * 初始化布隆过滤器,初始化失败,服务启动失败,需要处理异常
     */
    @PostConstruct
    public void initBloom() {
        try {
            boolean creationResult = create(Constants.DEVICE_IMEI_BLOOM_FILTER, "0.00000001", "5000000", 2);
            if (!creationResult) {
                throw new RuntimeException("Failed to initialize bloom filter.");
            }
            LOGGER.info("Successfully initialized bloom filter: {}", Constants.DEVICE_IMEI_BLOOM_FILTER);
        } catch (Exception e) {
            LOGGER.error("Error occurred during initialization of bloom filter: ", e);
            throw new RuntimeException("Initialization failed due to an error.", e);
        }
    }

    /**
     * 创建一个新的布隆过滤器
     *
     * @param filterName        布隆过滤器名称
     * @param falsePositiveRate 误报率,小于1,值越大,误报率越高,千万分之一误报率0.00000001
     * @param capacity          容量,容量如果小于写入数据,并且不自增,误报率会加大
     * @param autoCapacity      容量是否自增长,自增长的值,如果是1,就是不会自增长,可以设置为2
     */
    private boolean create(String filterName, String falsePositiveRate, String capacity, Integer autoCapacity) {
        if (StringUtils.isEmpty(filterName) || StringUtils.isEmpty(falsePositiveRate) || StringUtils.isEmpty(capacity)) {
            LOGGER.error("The base configuration for creating a bloom filter is empty filterName:{} falsePositiveRate:{} capacity:{}", filterName, falsePositiveRate, capacity);
            return false;
        }
        try {
            if (isBloomFilterExists(filterName)) {
                // 布隆过滤器已存在,无需再次创建
                return true;
            }
            String result;
            if (autoCapacity == null || autoCapacity <= 1) {
                result = jedisPool.getResource().eval(
                        "return redis.call('BF.RESERVE', KEYS[1], ARGV[1], ARGV[2])",
                        1, filterName, falsePositiveRate, capacity).toString();
            } else {
                result = jedisPool.getResource().eval(
                        "return redis.call('BF.RESERVE', KEYS[1], ARGV[1], ARGV[2], 'EXPANSION', ARGV[3])",
                        1, filterName, falsePositiveRate, capacity, autoCapacity.toString()).toString();
            }
            LOGGER.info("Create bloom result:{} filterName:{} falsePositiveRate:{} capacity:{} autoCapacity:{}", result, filterName, falsePositiveRate, capacity, autoCapacity);
            return OK.equals(result);
        } catch (Exception e) {
            LOGGER.error("Error during bloom filter creation for filter name: {}", filterName, e);
            return false;
        }
    }

    private boolean isBloomFilterExists(String filterName) {
        Object value = null;
        try {
            value = jedisPool.getResource().eval(
                    "return redis.call('BF.INFO', KEYS[1])",
                    1,
                    filterName
            );
            // 如果返回值为空或者是一个空的哈希表,说明布隆过滤器不存在
            if (value == null) {
                return false;
            }
            LOGGER.info("Bloom filter already exists value:{}", JacksonUtils.toJson(value));
            // 返回值不为空且不是空哈希表,说明布隆过滤器存在
            return true;
        } catch (Exception e) {
            // 处理其他可能的异常
            LOGGER.info("checking bloom filter not exist");
            return false;
        }
    }

    /**
     * 向布隆过滤器中添加元素
     *
     * @param filterName
     * @param value
     */
    public boolean set(String filterName, String value) {
        Long result = execute("BF.ADD", filterName, value);
        //一般1是成功,0是已存在
        LOGGER.info("Write bloom filter result:{} filterName:{} value:{}", result, filterName, value);
        return result != null;
    }

    /**
     * 批量写入
     *
     * @param filterName
     * @param imeiList
     * @return
     */
    public List<Long> batchSet(String filterName, List<String> imeiList) {
        StringBuilder luaScript = new StringBuilder();
        luaScript.append("local result = {}\n");
        luaScript.append("for i = 1, #ARGV do\n");
        luaScript.append("    table.insert(result, redis.call('BF.ADD', KEYS[1], ARGV[i]))\n");
        luaScript.append("end\n");
        luaScript.append("return result\n");
        List<Long> resultList = (List<Long>) jedisPool.getResource().eval(luaScript.toString(), Arrays.asList(filterName), imeiList);
        LOGGER.info("Batch write bloom filter result:{} filterName:{} value:{}", resultList, filterName, imeiList);
        return resultList;
    }

    /**
     * 检查元素是否可能存在于布隆过滤器中
     *
     * @return
     */
    public boolean exist(String filterName, String value) {
        Long result = execute("BF.EXISTS", filterName, value);
        //一般1是成功,0是已存在
        LOGGER.info("exist bloom filter result:{} filterName:{} value:{}", result, filterName, value);
        return SUCCESS.equals(result);
    }

    private Long execute(String command, String filterName, String value) {
        if (StringUtils.isEmpty(filterName) || StringUtils.isEmpty(value)) {
            LOGGER.error("Bloom filter parameter is empty filterName:{} value:{}", filterName, value);
            return null;
        }
        return (Long) jedisPool.getResource().eval(
                "return redis.call('" + command + "', KEYS[1], ARGV[1])",
                1, filterName, value
        );
    }
}



评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值