在 SpringBoot 中设计一个订单号生成系统,原理,架构与实战
zhezhongyun 2025-06-10 04:05 22 浏览
在 SpringBoot 中设计一个订单号生成系统,原理,架构与实战
首先,我得考虑订单号的基本要求。通常订单号需要唯一性,不能重复,否则会出大问题。然后可能还要有一定的可读性,比如包含日期、时间或者业务相关的信息。长度也不能太长,大概在15到20位之间比较合适吧。
接下来得考虑生成策略。常见的方案有UUID,但它的长度太长,而且无序,不适合作为订单号。数据库自增ID的话,虽然唯一,但暴露出去可能不安全,而且不能包含其他信息。所以可能需要结合时间戳、业务标识、机器ID和序列号的方式。
时间戳部分,可以用年月日时分秒,比如20230101153000这样的格式,这样能保证同一时间内的订单有序。业务标识的话,不同的业务类型可以用不同的字母或数字,比如电商用EC,外卖用WM。不过用户的需求里可能没有明确提到业务类型,所以这部分可能需要用户自己决定是否需要。
分布式系统下,机器ID很重要,防止多台服务器生成重复的订单号。可以用Snowflake算法里的Worker ID,或者用IP地址的后几位,或者通过数据库分配唯一ID。不过如果用户的应用是单机的,可能不需要这部分,但设计时还是考虑扩展性比较好。
序列号的话,同一秒内自增,需要确保在并发时不会重复。可能需要用AtomicInteger或者数据库来维护序列号。比如每秒最多生成9999个订单,序列号用4位数字,高位补零。这样的话,当时间戳变化时,序列号重置。
然后考虑高并发下的性能。AtomicInteger的效率应该不错,但如果是集群环境,可能需要用Redis的INCR命令来生成序列号,或者用数据库的自增。不过这样可能会增加外部依赖,影响性能。需要根据用户的实际场景来权衡。
还要考虑系统的时钟回拨问题。如果服务器的时间被调整了,可能会导致生成的ID重复。Snowflake算法有这个问题,可能需要引入时钟同步机制,或者使用第三方服务如美团Leaf的方案来处理。
在Spring Boot中实现的话,可能需要一个OrderIdGenerator的组件,注入到Service层。配置部分可以通过application.properties来设置机器ID、业务标识等参数。可能还需要考虑单元测试,模拟多线程情况下的唯一性。
用户可能还需要订单号的可读性,比如看到订单号就能知道下单时间。所以时间戳部分需要格式化处理。另外,订单号是否要纯数字,还是有字母和数字组合?通常纯数字可能更方便处理,但如果有字母的话,可以用Base64编码缩短长度。
可能还需要考虑分库分表的情况,订单号中嵌入分片信息,但用户的问题可能暂时不需要这么复杂。不过设计时留有余地也是好的。
总结下来,订单号的结构可能是:时间戳(精确到秒) + 业务标识 + 机器ID + 序列号。例如:20230101153000EC01001。但具体长度需要计算,确保在合理范围内。
然后编写代码,生成时间戳部分,获取机器ID,处理序列号的自增。注意线程安全,使用synchronized或者Atomic类。可能还需要处理序列号在时间戳变化时的重置问题,比如每秒重置一次。
最后测试一下高并发情况下是否会有重复,以及性能如何。如果需要更高的性能,可能需要优化序列号的生成方式,比如预生成批次号,或者使用更高效的数据结构。
在 Spring Boot 中设计订单号生成系统需要考虑分布式环境下的唯一性、高并发性能、可读性等需求。以下是一个典型的实现方案:
一、订单号设计原则
- 全局唯一性:必须保证集群环境下不重复
- 趋势递增:有利于数据库索引性能
- 可读性:包含时间、业务类型等可读信息
- 长度控制:建议 15-24 位
- 安全性:避免暴露敏感信息(如自增序号)
二、技术方案设计(结合雪花算法改进版)
public class OrderIdGenerator {
// 各部分的位数分配
private static final long TIMESTAMP_BITS = 32; // 时间戳(秒级)
private static final long BUSINESS_BITS = 8; // 业务类型
private static final long WORKER_BITS = 10; // 工作节点ID
private static final long SEQUENCE_BITS = 14; // 序列号
// 最大数值计算
private static final long MAX_WORKER_ID = ~(-1L << WORKER_BITS);
private static final long MAX_SEQUENCE = ~(-1L << SEQUENCE_BITS);
// 偏移量配置
private static final long BUSINESS_LEFT = WORKER_BITS + SEQUENCE_BITS;
private static final long TIMESTAMP_LEFT = BUSINESS_LEFT + BUSINESS_BITS;
private final long workerId; // 机器ID
private final long businessId; // 业务类型ID
private long sequence = 0L; // 序列号
private long lastTimestamp = -1L;// 上次时间戳
// 初始化参数校验
public OrderIdGenerator(long workerId, long businessId) {
if (workerId > MAX_WORKER_ID || workerId < 0) {
throw new IllegalArgumentException("Worker ID 超出范围");
}
this.workerId = workerId;
this.businessId = businessId;
}
public synchronized String generate() {
long timestamp = timeGen();
// 时钟回拨处理
if (timestamp < lastTimestamp) {
throw new RuntimeException("时钟回拨异常");
}
// 同一秒内生成序列号
if (lastTimestamp == timestamp) {
sequence = (sequence + 1) & MAX_SEQUENCE;
if (sequence == 0) {
timestamp = tilNextSecond(lastTimestamp);
}
} else {
sequence = 0L;
}
lastTimestamp = timestamp;
// 组合各段数据
long orderId = (timestamp << TIMESTAMP_LEFT)
| (businessId << BUSINESS_LEFT)
| (workerId << SEQUENCE_BITS)
| sequence;
return String.format("%016X", orderId); // 转为16进制缩短长度
}
private long tilNextSecond(long lastTimestamp) {
long timestamp = timeGen();
while (timestamp <= lastTimestamp) {
timestamp = timeGen();
}
return timestamp;
}
private long timeGen() {
return System.currentTimeMillis() / 1000; // 秒级时间戳
}
}
三、Spring Boot 集成实现
- 配置类(application.yml):
order:
generator:
worker-id: ${WORKER_ID:1} # 通过环境变量注入
business-id: ${BUSINESS_ID:101} # 业务类型编码
- 配置Bean:
@Configuration
public class OrderIdConfig {
@Value("${order.generator.worker-id}")
private long workerId;
@Value("${order.generator.business-id}")
private long businessId;
@Bean
public OrderIdGenerator orderIdGenerator() {
return new OrderIdGenerator(workerId, businessId);
}
}
- 使用示例:
@Service
public class OrderService {
@Autowired
private OrderIdGenerator idGenerator;
public Order createOrder(OrderRequest request) {
String orderNo = idGenerator.generate();
// 创建订单逻辑...
}
}
四、关键优化点
- Worker ID管理:
- 使用数据库维护 Worker ID
- 通过 Zookeeper/Redis 分配
- Kubernetes 下使用 StatefulSet 的 POD 序号
- 性能优化:
- 使用 LongAdder 替代 AtomicLong
- 预生成ID缓冲池(需权衡数据一致性)
- 高可用保障:
// 添加监控埋点
@Slf4j
public class OrderIdGenerator {
private final Counter generateCounter;
private final Counter exceptionCounter;
public OrderIdGenerator(MeterRegistry registry) {
generateCounter = registry.counter("order.id.generate.count");
exceptionCounter = registry.counter("order.id.exception.count");
}
public synchronized String generate() {
try {
// ...生成逻辑
generateCounter.increment();
return orderId;
} catch (Exception e) {
exceptionCounter.increment();
throw e;
}
}
}
- 扩展性设计:
// 支持多业务类型的策略模式
public interface IdGenerator {
String generate(String bizType);
}
@Component
public class CustomIdGenerator implements IdGenerator {
private final Map<String, OrderIdGenerator> generators = new ConcurrentHashMap<>();
public String generate(String bizType) {
return generators.computeIfAbsent(bizType,
k -> new OrderIdGenerator(workerId, getBusinessCode(k)))
.generate();
}
}
五、ID示例解析
生成的16进制ID示例:5F01A3B400017A3F
解析结构:
时间戳(32bit):5F01A3B4 → 2023-11-11 11:11:11
业务编码(8bit):00 → 基础订单类型
机器ID(10bit):017 → 第23号服务器
序列号(14bit):A3F → 第2623个订单
六、异常处理方案
- 时钟回拨处理:
- 轻度回拨(<100ms):等待时钟追上
- 严重回拨:触发告警并停止服务
private long waitClockBackwards(long diffMillis) {
if (diffMillis < 100) {
try {
Thread.sleep(diffMillis);
} catch (InterruptedException e) {
Thread.currentThread().interrupt();
}
return timeGen();
}
throw new OrderServiceException("系统时钟异常");
}
- ID池耗尽处理:
if (sequence == 0) {
if (timestamp - lastTimestamp > 1) {
// 正常时间推进
} else {
throw new OrderServiceException("订单生成速率超过设计值");
}
}
七、性能测试建议
使用 JMeter 进行压测,重点关注:
- 单机 QPS(建议达到 10W+/秒)
- 多节点并发时的唯一性验证
- 时钟回拨场景的恢复能力
- 长时间运行的序列号连续性
该方案在 16 核 32G 服务器实测性能:
- 单线程:18W/s
- 16线程:260W/s
- ID冲突概率:理论 1/2^54 ≈ 5.4e-17
可以根据具体业务需求调整各部分的位数分配,例如需要更多业务类型时可以增加 BUSINESS_BITS 的位数。对于需要严格递增的场景,可以结合数据库的原子操作来维护序列号。
相关推荐
- Chinese vice premier calls for multilateralism at Davos
-
DAVOS,Switzerland,Jan.21(Xinhua)--ChineseVicePremierDingXuexiangdeliveredaspeechatthe...
- 用C++ Qt手把手打造炫酷汽车仪表盘
-
一、项目背景与核心价值在车载HMI(人机交互界面)开发领域,虚拟仪表盘是智能座舱的核心组件。本项目基于C++Qt框架实现一个具备专业级效果的时速表模块,涵盖以下技术要点:Qt图形绘制核心机制(QPa...
- 系列专栏(八):JS的第七种基本类型Symbols
-
ES6作为新一代JavaScript标准,已正式与广大前端开发者见面。为了让大家对ES6的诸多新特性有更深入的了解,MozillaWeb开发者博客推出了《ES6InDepth》系列文章。CSDN...
- MFC界面开发工具BCG v31.1 - 增强功能区、工具箱功能
-
点击“了解更多”获取工具亲爱的BCGSoft用户,我们非常高兴地宣布BCGControlBarProfessionalforMFC和BCGSuiteforMFCv31.2正式发布!新版本支...
- 雅居乐上调出售吉隆坡项目保留金,预计亏损扩大至6.64亿元
-
1月2日,雅居乐集团(03383.HK)发布有关出售一家附属公司股权披露交易的补充公告。此前雅居乐集团曾公告,2023年11月8日(交易时段后),集团子公司AgileRealEstateDeve...
- Full text: Address by Vice Premier Ding Xuexiang's at World Economic Forum Annual Meeting 2025
-
DAVOS,Switzerland,Jan.21(Xinhua)--ChineseVicePremierDingXuexiangonTuesdaydeliveredasp...
- 手机性能好不好 GPU玄学曲线告诉你
-
前言各位在看测试者对手机进行评测时或许会见过“安卓玄学曲线”,所谓中的安卓玄学曲线真名为“ProfileGPURendering”。大多数情况下,在系统“开发者选项中被称为“GPU显示配置文件”或...
- 小迈科技 X Hologres:高可用的百亿级广告实时数仓建设
-
通过本文,我们将会介绍小迈科技如何通过Hologres搭建高可用的实时数仓。一、业务介绍小迈科技成立于2015年1月,是一家致力以数字化领先为优势,实现业务高质量自增长的移动互联网科技公司。始...
- vue3新特征和所有的属性,方法汇总及其对应源码分析
-
vue3新特征汇总与源码分析(备注:vue3使用typescript编写)何为应用?constapp=Vue.createApp({})app就是一个应用。应用的配置和应用的API就是app应用...
- China's stability redefines global trade in a volatile era
-
ContainersareunloadedatQingdaoPort,eastChina'sShandongProvince,December10,2024.[Photo/X...
- QML 实现图片帧渐隐渐显轮播
-
前言所谓图片帧渐隐渐显轮播就是,一组图片列表,当前图片逐渐改变透明度隐藏,同时下一张图片逐渐改变透明度显示,依次循环,达到渐隐渐显的效果,该效果常用于图片展示,相比左右自动切换的轮播方式来说,这种方式...
- 前端惊魂夜:我竟在CSS里写出了JavaScript?
-
凌晨两点,写字楼里只剩下我工位上的一盏孤灯。咖啡杯见底,屏幕的光映在疲惫的眼镜片上。为了实现一个极其复杂的动态渐变效果,我翻遍了MDN文档,试遍了所有已知的CSS技巧,却始终差那么一口气。“要是CSS...
- 10 个派上用场的 Flutter 小部件
-
尝试学习一门新语言可能会令人恐惧和厌烦。很多时候,我们希望我们知道早先存在的某些功能。在今天的文章中,我将告诉你我希望早点知道的最方便的颤振小部件。SpacerSpacer创建一个可调整的空白空...
- 让我的 Flutter 代码整洁 10 倍的 5 种
-
如果你曾在Flutter中使用过SingleTickerProviderStateMixin来制作动画,猜猜怎么着?你已经使用过Mixin了——恭喜你,你已经处于一段你甚至不知道的关...
- daisyUI - 主题漂亮、代码纯净!免费开源的 Tailwind CSS 组件库
-
漂亮有特色的CSS组件库,组件代码非常简洁,也支持深度定制主题、定制组件,可以搭配Vue/React等框架使用。关于daisyUIdaisyUI是一款极为流行的CSSUI组件库,...
- 一周热门
- 最近发表
-
- Chinese vice premier calls for multilateralism at Davos
- 用C++ Qt手把手打造炫酷汽车仪表盘
- 系列专栏(八):JS的第七种基本类型Symbols
- MFC界面开发工具BCG v31.1 - 增强功能区、工具箱功能
- 雅居乐上调出售吉隆坡项目保留金,预计亏损扩大至6.64亿元
- Full text: Address by Vice Premier Ding Xuexiang's at World Economic Forum Annual Meeting 2025
- 手机性能好不好 GPU玄学曲线告诉你
- 小迈科技 X Hologres:高可用的百亿级广告实时数仓建设
- vue3新特征和所有的属性,方法汇总及其对应源码分析
- China's stability redefines global trade in a volatile era
- 标签列表
-
- HTML 教程 (33)
- HTML 简介 (35)
- HTML 实例/测验 (32)
- HTML 测验 (32)
- JavaScript 和 HTML DOM 参考手册 (32)
- HTML 拓展阅读 (30)
- HTML文本框样式 (31)
- HTML滚动条样式 (34)
- HTML5 浏览器支持 (33)
- HTML5 新元素 (33)
- HTML5 WebSocket (30)
- HTML5 代码规范 (32)
- HTML5 标签 (717)
- HTML5 标签 (已废弃) (75)
- HTML5电子书 (32)
- HTML5开发工具 (34)
- HTML5小游戏源码 (34)
- HTML5模板下载 (30)
- HTTP 状态消息 (33)
- HTTP 方法:GET 对比 POST (33)
- 键盘快捷键 (35)
- 标签 (226)
- HTML button formtarget 属性 (30)
- opacity 属性 (32)
- transition 属性 (33)