安装RedisBloom模块,从git获取对应的原码,make生成.so文件,挂载.so文件,启动redis
powershell
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
java
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
);
}
}