业务需求:创建一个流水号,
功能说明:
1,格式:orgCode + yyyyMMdd + 4位序号,
2,每天重新从 0001 开始
3,使用 Redisson 保证并发下分布式唯一,
4,Redis 存储当天序列号
示例代码
- Maven 依赖(如果还没加 Redisson):
java
<dependency>
<groupId>org.redisson</groupId>
<artifactId>redisson</artifactId>
<version>3.17.7</version>
</dependency>
- 配置 RedissonClient(示例单节点):
java
@Configuration
public class RedissonConfig {
@Bean
public RedissonClient redissonClient() {
Config config = new Config();
config.useSingleServer()
.setAddress("redis://127.0.0.1:6379") // 修改为你自己的 Redis 地址
.setDatabase(0);
return Redisson.create(config);
}
}
- 流水号工具类:
java
@Component
public class SerialNumberGenerator {
@Autowired
private RedissonClient redissonClient;
private static final String SERIAL_KEY_PREFIX = "serial:";
public String generateSerialNumber(String orgCode) {
String currentDate = new SimpleDateFormat("yyyyMMdd").format(new Date());
String key = SERIAL_KEY_PREFIX + currentDate;
String lockKey = "lock:serial:" + currentDate;
RLock lock = redissonClient.getLock(lockKey);
try {
// 获取锁,最多等待5秒,锁自动释放时间为10秒
if (lock.tryLock(5, 10, TimeUnit.SECONDS)) {
RAtomicLong counter = redissonClient.getAtomicLong(key);
// 如果是新的一天,重置为0
if (!counter.isExists()) {
counter.set(0);
// 设置过期时间为2天,避免key一直存在
counter.expire(2, TimeUnit.DAYS);
}
long serial = counter.incrementAndGet();
String formattedSerial = String.format("%04d", serial);
return orgCode + currentDate + formattedSerial;
} else {
throw new RuntimeException("生成流水号失败:获取锁超时");
}
} catch (InterruptedException e) {
throw new RuntimeException("生成流水号失败:线程中断", e);
} finally {
if (lock.isHeldByCurrentThread()) {
lock.unlock();
}
}
}
}
为什么需要 Redis 分布式锁?
不加锁的问题 :
Redis 是高并发环境下的共享存储,多个实例并发 get + set 操作有可能导致并发写丢失或重复编号。
加锁保证 :每次生成编号时只有一个线程在操作该 key 的值,从而保证原子性和唯一性