一、需求背景
项目中需要保证订单号唯一性,保证准确率和速度的前提下,可以使用redis的redisson布隆过滤器来实现。
缺点:存在误判率。使用时跟产品经理确认是否允许出现误判的情况。
二、实战代码
1.开启redis的redisson配置
/*application.yml中redis配置开启redisson.enable: true*/
redis:
redisson:
enable: true
host: 127.0.0.1
port: 6379
timeout: 6000
database: 102
lettuce:
pool:
max-active: 10 # 连接池最大连接数(使用负值表示没有限制),如果赋值为-1,则表示不限制;如果pool已经分配了maxActive个jedis实例,则此时pool的状态为exhausted(耗尽)
max-idle: 8 # 连接池中的最大空闲连接 ,默认值也是8
max-wait: 500 # # 等待可用连接的最大时间,单位毫秒,默认值为-1,表示永不超时。如果超过等待时间,则直接抛出JedisConnectionException
min-idle: 5 # 连接池中的最小空闲连接 ,默认值也是0
shutdown-timeout: 500ms
2.实现布隆过滤器工具类
package com.xx.db.service;
import com.bizmda.bizsip.common.BizException;
import com.fundpay.common.enums.BusResultEnum;
import com.open.capacity.redis.RedisAutoConfig;
import org.redisson.api.RBloomFilter;
import org.redisson.api.RedissonClient;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;
import java.io.IOException;
/**
* @ClassName RedisUtil
* @Description redisson 布隆过滤器工具类
* @Author whb
* @Date 2023/2/9 9:27
* @Version 1.0
**/
@Component
public class BloomFilterService {
@Autowired
private RedissonClient redisson;
/**
* 布隆过滤器初始化
*
* @param bloomFilterName 过滤器名称
*/
public void bloomFilterInit(String bloomFilterName) {
RBloomFilter<String> bloomFilter= redisson.getBloomFilter(bloomFilterName);
//布隆过滤器计算的正确率为97%,初始化布隆过滤器容量为50000L
bloomFilter.tryInit(500000L,0.03);
}
/**
* 布隆过滤器添加数据
*
* @param bloomFilterName 过滤器名称
* @param value 要添加的值
*/
public boolean bloomFilterAdd(String bloomFilterName, String value) {
RBloomFilter<Object> bloomFilter= redisson.getBloomFilter(bloomFilterName);
return bloomFilter.add(value);
}
/**
* 布隆过滤器数据统计
*
* @param bloomFilterName
* @param value
*/
public boolean bloomFilterContains(String bloomFilterName, String value) throws BizException {
RBloomFilter<Object> bloomFilter= redisson.getBloomFilter(bloomFilterName);
return bloomFilter.contains(value);
}
}
我这里的RedissonClient 是实现过了,如果项目中没有实现,需要实现下,代码基本如下:
//1 创建redission的config对象并配置redis服务器(此处使用singleServer)
Config config = new Config();
config.useSingleServer().setAddress("redis://localhost:6379");
//2 创建redis客户端redissionClient
RedissonClient redissonClient = Redisson.create(config);
3.工具类使用
/**
* RedisKeyDef.ORDER_NO_BLOOM 是自行定义redis里面的分组(视自己项目需不需要分组)
* bloomFilterName 定义的过滤器的key唯一的
* ordNo 需要校验的订单号
*/
private void doBloomFilter(String bloomFilterName, String ordNo) throws BizException {
bloomFilterName = RedisKeyDef.ORDER_NO_BLOOM+bloomFilterName;
bloomFilterService.bloomFilterInit(bloomFilterName);
//加入过滤器
if (!bloomFilterService.bloomFilterAdd(bloomFilterName, ordNo)) {
throw new BaseException("01", "订单号" + ordNo + "已存在");
}
}