RedisBloom使用
安装RedisBloom模块,从git获取对应的原码,make生成.so文件,挂载.so文件,启动redis
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/redismodule.so
package com.jimi.rtvos.helper;import com.google.common.collect.Lists;
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.nio.charset.StandardCharsets;
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 String SUCCESS = "1";@Resourceprotected JedisPool jedisPool;/*** 初始化布隆过滤器,初始化失败,服务启动失败,需要处理异常*/@PostConstructpublic 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.error("Error when checking bloom filter existence: ", e);return false;}}/*** 向布隆过滤器中添加元素** @param filterName* @param value*/public boolean set(String filterName, String value) {Long result = execute("BF.ADD", filterName, value);//一般1是成功,0是已存在LOGGER.error("Write bloom filter result:{} filterName:{} value:{}", result, filterName, value);return result != null;}/*** 检查元素是否可能存在于布隆过滤器中** @return*/public boolean exist(String filterName, String value) {Long result = execute("BF.EXISTS", filterName, value);//一般1是成功,0是已存在LOGGER.error("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);}
}