如何生成按日重置的顺序唯一Application Number并解决并发冲突
业务申请唯一序列号生成及并发冲突解决方案
核心规则适配
适配要求生成的编号格式为APP-日期-4位序列号,满足当日序列号持续递增、次日自动从0001重新计数的需求,同时彻底解决并发重复问题。
主流实现方案
1 关系型数据库实现(适用90%以上常规业务场景)
依赖数据库的事务和行锁机制保证原子性,实现成本低、数据一致性高:
- 第一步:创建专用序列号生成表,示例建表语句如下:
CREATE TABLE `application_seq` ( `date_key` varchar(8) NOT NULL COMMENT '日期主键,格式为yyyyMMdd', `current_seq` int NOT NULL DEFAULT '0' COMMENT '当前已分配的最大序列号', PRIMARY KEY (`date_key`) ) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4;
- 第二步:每次生成编号时开启事务,执行
SELECT current_seq FROM application_seq WHERE date_key = '当前日期字符串' FOR UPDATE给当日行加排他锁,若查询无结果则插入当日初始记录,current_seq默认值为0 - 第三步:执行
UPDATE application_seq SET current_seq = current_seq + 1 WHERE date_key = '当前日期字符串',拿到更新后的序列号 - 第四步:将序列号补前导0至4位,按
APP-YYYYMMDD-xxxx格式拼接得到最终申请号,提交事务即可 - 兜底配置:给业务表的
application_number字段添加唯一索引,极端场景下也会拒绝重复数据落库
2 Redis实现(适用高并发分布式场景)
依赖Redis原生原子操作实现,性能远高于数据库方案,适合qps较高的业务:
- 以日期为key存储当日序列号,key命名规则示例:
app_seq:20240520 - 每次生成编号时直接执行
INCR app_seq:当前日期命令,该操作是原子性的,天然规避并发冲突,返回值即为本次可用的序列号 - 给每个日期key设置48小时过期时间,次日新请求会自动生成新的key从1开始计数,过期的历史key自动清理
- 同数据库方案,需要在业务表侧给申请号加唯一索引作为兜底,避免Redis故障、数据丢失时出现重复编号
并发冲突规避核心要点
并发重复的根因是多个请求同时读取到同一个序列号,并行执行加1操作导致生成相同编号,规避时需注意以下几点:
- 禁止单独使用服务内存存储序列号,集群部署时内存不共享必然会出现重复
- 锁粒度仅控制在单日维度,不要锁全表,避免影响序列号生成性能
- 所有序列号更新操作必须是原子性的,禁止用先查再改的非原子逻辑
- 若业务允许少量跳号,优先选择无回收逻辑的方案,性能更高;若严格要求连续不跳号,可增加失效序列号回收池,分配时优先取回收池内的序列号
内容的提问来源于stack exchange,提问作者user3913335
相关产品推荐
相关产品推荐

