在分布式系统中,编码生成是一个高频且核心的场景 ------ 订单号、流水号、设备编码、业务单据号等,都要求全局唯一、规则可配置、高性能、支持定时重置。传统的数据库自增方案存在性能瓶颈,简单的本地生成又无法保证分布式唯一性,而硬编码的规则又缺乏灵活性。
本文将分享一套基于 Redis+Redisson 实现的分布式编码生成器,该方案兼顾灵活性、高性能和分布式一致性,支持自定义编码规则、分布式唯一流水号、定时重置流水号等核心能力,可直接落地到生产环境。
核心需求与设计目标
在设计编码生成器前,我们先明确核心需求:
- 规则灵活:支持时间(年 / 月 / 日 / 时 / 分 / 秒 / 毫秒)、自增流水号、随机字符等占位符,自定义各部分长度;
- 分布式唯一:多实例部署下,生成的编码必须全局唯一,无重复;
- 高性能:支撑高并发场景,减少数据库依赖,降低 IO 开销;
- 容错兜底:Redis 重启 / 清空后,流水号能从数据库恢复,避免重复;
- 可重置:支持按 Cron 表达式定时重置流水号(如每日零点重置为 0);
- 易扩展:新增占位符规则时,代码改动最小化。
核心技术栈
基础框架:Spring Boot + MyBatis-Plus(数据持久化);
分布式缓存:Redis(流水号自增、规则缓存);
分布式锁:Redisson(保证并发安全、定时任务幂等);
定时任务:ThreadPoolTaskScheduler + Cron 表达式(流水号重置);
工具类:Hutool(Cron 解析、字符串处理);
性能优化:本地缓存 + Redis 缓存、异步批量持久化。
核心功能与实现原理
编码规则设计
编码规则采用 "固定字符 + 占位符" 的形式,支持以下占位符(可自定义扩展):
| 占位符 | 含义 | 示例(长度 4) |
|---|---|---|
| ${yyyy} | 年 | 2026 |
| ${MM} | 月(补零) | 01 |
| ${dd} | 日(补零) | 08 |
| ${hh} | 时(24 小时制) | 14 |
| ${mm} | 分(补零) | 59 |
| ${ss} | 秒(补零) | 07 |
| ${S} | 毫秒(补零) | 123(最大 3 位) |
| ${X} | 自增流水号 | 0001、00002 |
| ${UUU} | 随机字符 | A87、9z6 |
示例规则:JPC${hh}${UUU}${mm}${ss}${XXXX} → 生成结果:JPC14A8759070001。
核心模块实现
规则解析模块
规则解析是编码生成的基础,核心逻辑通过栈结构 解析占位符,提取占位符类型和长度,代码核心逻辑在parseCodeRule和generateConfigValue方法中:
- 遍历规则字符串,通过栈匹配
${}边界,提取内部的占位符(如XXXX); - 校验占位符合法性(必须是连续相同字符,如
XXXX而非XxXX); - 根据占位符类型(如
X= 流水号、U= 随机数)生成对应内容,补零或截取到指定长度。
核心亮点:解析逻辑与生成逻辑解耦,新增占位符只需在generateConfigValue的 switch 中添加分支,扩展性极强。
分布式流水号生成(核心)
流水号是编码唯一性的关键,采用 "Redis 自增 + 数据库兜底 + 分布式锁" 的方案:
Redis 自增 :通过redisTemplate.opsForValue().increment(seqKey, 1)实现高性能自增,Redis 的单线程特性保证自增原子性;
分布式锁防并发初始化:当 Redis 中流水号 KEY 不存在(自增返回 1)时,加 Redisson 分布式锁,从数据库查询该规则的最大流水号,初始化到 Redis,避免多实例并发初始化导致流水号重复;
数据库兜底 :Redis 重启 / 清空后,流水号从数据库max_seq字段加载,保证流水号不回退、不重复;
核心代码片段(流水号生成):
private String generateSeqNum(CodeRule codeRule, int len) {
String code = codeRule.getCode();
String seqKey = REDIS_SEQ_PREFIX + code;
RLock initLock = redissonClient.getLock("seq:init:lock:" + code);
try {
Long seq = redisTemplate.opsForValue().increment(seqKey, 1);
if (seq == 1) { // 首次初始化
locked = initLock.tryLock(5, 10, TimeUnit.SECONDS);
if (locked) {
Long maxSeq = queryMaxSeqFromDb(code); // 从DB加载最大值
if (maxSeq > 0) {
redisTemplate.opsForValue().set(seqKey, maxSeq);
seq = redisTemplate.opsForValue().increment(seqKey, 1);
}
}
}
saveBySeq(codeRule, seq); // 异步持久化
return String.format("%0" + len + "d", seq);
} finally {
if (locked) initLock.unlock();
}
}
定时重置流水号
支持按 Cron 表达式(如0 0 0 * * ?每日零点)重置流水号,核心类为SeqCronTaskManager
- 分布式定时任务 :基于
ThreadPoolTaskScheduler创建定时任务,多实例部署下通过分布式锁保证同一时间只有一个实例执行重置; - 幂等处理:通过 Redis 设置执行标识,避免同一 Cron 周期内重复重置;
- 统一 KEY 规则 :重置的流水号 KEY 统一为
CODE_RULE_SEQ:规则标识,避免特殊字符导致的 Redis 客户端解析异常。
性能优化策略
- 多级缓存 :编码规则先查本地缓存(
CODE_RULE_CACHE),再查 Redis,最后查数据库,减少 DB 查询; - 异步批量持久化:流水号不是每次生成都写库,而是通过原子计数器累计到 100 次后异步批量更新,降低数据库 IO;
接口层设计
提供 RESTful 接口,支持编码规则的 CRUD、缓存刷新、编码生成等,核心生成接口:
GET /code_rule/generateCode?code=JPC
请求示例:调用/code_rule/generateCode?code=JPC,规则为JPC${hh}${UUU}${mm}${ss}${XXXX},返回JPC14A8759070001。
异常设计
异常处理
-
分布式锁超时:抛出友好提示,避免无限阻塞;
-
Redis 类型异常:捕获 WRONGTYPE 异常,提示清理错误 KEY;
-
空指针防护:缓存查询、数据库查询后均做非空校验;
-
线程中断:捕获 InterruptedException,恢复线程中断状态。
redis配置
Redis 序列化配置(避免值序列化后为二进制,导致自增失败):
@Bean
public RedisTemplate<String, Object> redisTemplate(RedisConnectionFactory factory) {
RedisTemplate<String, Object> template = new RedisTemplate<>();
template.setKeySerializer(new StringRedisSerializer());
template.setValueSerializer(new GenericJackson2JsonRedisSerializer());
template.setConnectionFactory(factory);
return template;
}
完整代码地址
https://gitee.com/zh2358853434/short-chain.git
总结
这套编码生成器方案解决了系统中编码生成的核心痛点:
- 灵活性:通过占位符支持自定义规则,新增规则成本低;
- 唯一性:Redis 自增 + Redisson 分布式锁保证分布式唯一;
- 高性能:多级缓存 + 异步持久化,支撑高并发;
- 容错性:数据库兜底 + 类型校验,避免流水号丢失或异常;
- 可配置:Cron 表达式支持灵活的流水号重置策略。