如何设计一个订单号生成服务?应该考虑那些问题?

news/2025/3/29 11:23:26/

如何设计一个订单号生成服务?应该考虑那些问题?
description: 在高并发的电商系统中,生成全局唯一的订单编号是关键。本文探讨了几种常见的订单编号生成方法,包括UUID、数据库自增、雪花算法和基于Redis的分布式组件,并详细分析了它们的优缺点。让你在面试过程中更进一步。
这边先来看一个场景:

在实际开发过程中,业务中最常见的一个服务就是生成业务单号,比如电商订单编号、入库单号、服务单号等等。那么如果让你创建一个订单号,你会考虑那些问题?有什么好的设计思路嘛?

需求分析

考虑一下如果让你设计一个服务单号,需要满足那些基本需求呢?、

  • 全局唯一性: 订单号需要在在整个系统独一无二,避免重复
  • 安全性: 订单号不能暴漏太多信息,比如流水信息、用户信息,那么自增这种方案就不能考虑了
  • 禁用随机码: 随机码虽然可以满足前两个条件,但是随机码没有更多信息,比如相对顺序、日期信息,同时伴有一定概率会重复
  • 满足并发需求: 像在特定场合下(如秒杀)需要做到并发场景下的订单号的生成
  • 控制位数: 订单号的位数尽量在 10 位 ~ 18 位之间。太短的情况下,如果交易量过大,很难做到防止重复,太长可读性差、意义也不大。
  • 有业务含义: 订单号尽可能包含业务信息,比如订单时间、业务类型等

再此基础上如何设计一个优秀的订单号服务呢?这个时候最好梳理一下功能点,需要满足那些要求:

设计目标:

  • 满足高并发需求
  • 可拓展性高
  • 满足峰值压力
  • 支持分布式架构
  • 检索效率,如果订单号存储在数据库中,检索效率需要考虑

设计方案

接下来会阐述一些常见的方案,再谈论一下这种解决方案的优缺点。

方案一:数据库自增ID

所谓数据库自增,意思是在数据库中给某个列设置为自增列,并且给该列设置一个初始值,代码层面无需任何特殊处理,以 Mysql 的用户表 ID 列为例,可以通过如下方式在创建表的时候生产。

CREATE TABLE `tb_user` (`id` bigint(20) unsigned NOT NULL AUTO_INCREMENT,`name` varchar(20) DEFAULT NULL,PRIMARY KEY (`id`)
) ENGINE=InnoDB AUTO_INCREMENT=1 DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_0900_ai_ci;

优点:

  • 实现简单
  • 保证唯一性

缺点:

  • 数据库存在性能瓶颈,特别是高并发情况下
  • 分布式数据库,在多个实例情况下难以保证全局唯一、
  • 安全性低,自增ID容易暴露信息
  • 无业务含义

适用场景:

如果是单体服务下,并且是初始业务情况下可以使用,但需要对数据库进行分库分表会出现重复ID。不建议直接使用这种方式,上限较低。

方案二:UUID

UUID 是Universally Unique Indentifier的缩写,翻译为通用唯一识别码,顾名思义 UUID 是一个用于记录唯一标识一条的数据,其按照开放软件基金会(OSF)指定的标准进行计算,用到了以太网卡地址(MAC)、纳秒级时间、芯片 ID 码和许多可能的数字。

总的来说,UUID 码由以下三部分组成:

  • 当前日期和时间
  • 时钟序列
  • 全局唯一的 IEEE 机器识别码(如果有网卡从网卡获得,没有网卡则通过其他方式获得)

UUID 的标准形式包含 32 个 16 进制数字,以连字号分为五段,示例:00000191-adc6-4314-8799-5c3d737aa7de

java为例,通过以下方式即可生成:

String uuid = UUID.randomUUID().toString();

优点:

  • 全局唯一性
  • 简单易用
  • 安全性: UUID的随机性使得它不容易被猜测或预测,增加了数据的安全性
  • 无需集中管理:由于UUID是本地生成的,不需要依赖于中心化的ID生成服务,减少了单点故障的风险。
  • 可扩展性:UUID的生成过程是分布式的,可以在多个节点上并行生成,适合大规模分布式系统。

缺点:

  • 长度较长
  • 可读性差: 无业务含义,难以理解
  • 索引效率低: UUID的随机性会导致插入时的索引分裂和碎片化,从而降低写入性能,查询效率也低
  • 排序问题: UUID不具备自然的时间顺序,因此不适合用于需要按时间顺序进行排序的场景

适用场景:

如果是在分布式系统中,对订单号有着特别高的要求,并且不需要长期持久化存储以及不需要频繁查询那么就可以推荐使用。

分布式文件存储系统,该系统需要处理大量的文件上传、下载和管理操作。这些文件可能来自不同的用户和设备,并且需要在多个服务器之间进行分布存储和访问。为了确保每个文件都有一个唯一的标识符,并且能够在全球范围内保持唯一性,UUID是一个非常适合的选择。

方案三: 雪花算法

Snowflake(中文简称:雪花算法) 是 Twitter 内部的一个 ID 生算法,可以通过一些简单的规则保证在大规模分布式情况下生成唯一的 ID 号码。Snowflake把 64-bit分别划分成多段,分开来标识机器ID、时间等。其核心思想是:使用 41bit作为毫秒数,10bit作为机器的ID(5个bit是数据中心,5个bit的机器ID),12bit作为毫秒内的流水号,最后还有一个符号位,永远是0。SnowFlake 的结构图如下所示:

SnowFlake

可以很清晰的看出,Snowflake 由 4个部分组成:

  • 第一部分:bit 值,为未使用的符号位
  • 第二部分:由 41 位的时间戳(毫秒)构成,它的取值是当前时间相对于某一时间的偏移
  • 第三部分:表示工作机器 id,由服务节点 id 和数据中心 id 组合而成
  • 第四部分:表示每个工作机器每毫秒生成的序列号 ID,同一毫秒内最多可生成生产 4095 个 ID。

由于在 Java 中 64bit 的整数是 long 类型,因此在 Java 中 SnowFlake 算法生成的 id 就是 long 来存储的。

SnowFlake 算法可以保证:

  • 1.所有生成的 id 按时间趋势递增
  • 2.整个分布式系统内不会产生重复id(因为有服务节点 id 和数据中心 id 来做区分)

需要注意的是:

  • 在分布式环境中,5 个 bit 位的 datacenter 和 worker 表示最多能部署 31 个数据中心,每个数据中心最多可部署 31 台节点。
  • 41 位的二进制长度最多能表示2^41 -1毫秒即 69 年,所以雪花算法最多能正常使用 69 年,为了能最大限度的使用该算法,在使用的时候,应该为其指定一个开始时间,不然会发生重复!

优点

  1. 高并发下的唯一性:由于算法设计考虑到了时间戳、机器标识等因素,因此即使是在大规模分布式系统中也能保证生成的ID是唯一的。
  2. 趋势递增:生成的ID通常是按照时间顺序递增的,这有利于数据库索引性能优化。
  3. 信息量丰富:ID中包含了时间戳、工作节点ID等信息,使得每个ID都携带了额外的信息价值。
  4. 不依赖数据库:相比于传统基于数据库自增ID的方式,雪花算法不需要访问数据库即可生成ID,减少了数据库的压力。
  5. 高效:计算效率高,适合快速生成大量ID的需求。

缺点

  1. 时钟回拨问题:如果服务器时钟出现回拨,则可能产生重复ID的问题。不过,可以通过等待时钟追赶上来或者拒绝服务来解决这个问题。
  2. 有限的空间:虽然64位足够大,但理论上还是存在上限,对于某些极端情况可能不够用。
  3. 可读性差:生成的ID对人类来说不易于阅读和理解。
  4. 安全性较低:因为包含时间戳,所以可能泄露一些关于数据创建时间的信息,这在某些安全敏感的应用场景下可能是不利的。

适用场景

  • 分布式系统:特别适用于需要跨多个服务器或数据中心工作的应用程序,如电商网站、社交平台等。
  • 高并发应用:当系统需要处理大量的并发请求并要求快速响应时,比如在线游戏、实时消息传递服务等。
  • 需要唯一标识符的服务:任何需要生成全局唯一标识符的服务都可以采用此方法,例如订单管理系统、用户账号注册等。
  • 大数据分析:由于ID带有时间信息,有助于进行时间序列数据分析。

方案四:借助Redis,分布式组件

要想在分布式环境下生成一个唯一的订单编号,我们可以通过分布式组件的方式,来帮忙我们生成全局唯一的订单号,例如我们可以采用 redis 分布式缓存组件中的incr命令,来帮我们生成一个全局自增长的序列号。

实现某个Key实现自增的代码如下:

// 基于某个key实现自增长
String res = jedis.get(key);
if (StringUtils.isBlank(res)) {// 设置初始值,INIT_ID 是初始值jedisClient.set(key, INIT_ID);// 设置过期时间,seconds 是多少秒过期jedisClient.expire(key, seconds);
}
//存在就生成+1的订单号
long orderId = jedis.incr(key);

这种方式生成的自增长序列号,非常的快,可以很好的满足大流量环境下的编号要求唯一的特性!

案例分析:

在互联网几个大厂的订单号分析一下:

  • 京东商城订单号格式:157444499

  • 苏宁易购订单号格式:2000839647

  • 凡客诚品订单号格式:213052230059

  • 小米订单号格式:1111218032345170

凡客诚品和银泰网订单号都含有 0522,这是因为这 2 张订单都是2013年5月22号下的订单。

基本猜测一下,凡客的订单规则是:业务编码+年的后2位+月+日+订单数;泰网的订单号规则:年的第三位数+业务编码+年的后1位+月+日+订单数;而京东商城和苏宁易购的订单号看不出规则。

再来分析一下小米订单编号:

1211218032345170(16位)//拆解成为四个部分
1——211218—03234—5170
  • 第一部分,1 表示购买,2 表示退货。
  • 第二部分,表示 2021 年 12 月 18 日下的单,前面两位省掉了。
  • 第三部分,时间戳对应00:53:54,换算成秒是03234秒。
  • 最后一部分,表示在同一秒内下的第 5170 单,也就是说,小米认为,在一秒内不会超过一万个订单。

总结

通过上面的示例演示,下面针对这几种情况做一个分析与总结。尽可能的选择一种合理的方式。

实现方案优势劣势
数据库自增代码层面无需任何特殊处理;利用MySQL特点实现数据递增并发性能差;MySQL负担重
UUID实现简单、方便;重复性低可读性低;过于冗长;数据库查询效率低
雪花算法基于内存、速度快;性能高;不会产生额外的网络开销;数据依次成递增依赖于服务器时间,如变动服务器时间则存在重复的情况
Redis基于内存、速度库;使用简单;可分布数据、扩展性强需要独立搭建一套服务、增加了维护成本;跨应用调用、存在网络开销

总体上来说,优先选择使用Redis进行分布式的处理方式,如果在没有Redis的情况下,那就优先选用雪花算法。

如果想要设计一个订单号,需要保留业务类型、时间信息等。

比如通过2位数字表示业务类型,如交易订单、支付单、结算单等都是不同的业务类型,可以有不同的编号。
中间的18-20位用一个唯一的ID来表示,可以用雪花算法,也可以用Leaf,总之就是他需要保证唯一性。
最后4位,基于基因法,将分表后的结果获取到,把他也编码到订单号中。


http://www.ppmy.cn/news/1583147.html

相关文章

2024年3月全国计算机等级考试真题(二级C语言)

😀 第1题 下列叙述中正确的是 A. 矩阵是非线性结构 B. 数组是长度固定的线性表 C. 对线性表只能作插入与删除运算 D. 线性表中各元素的数据类型可以不同 题目解析: A. 矩阵是非线性结构 错误。矩阵通常是二维数组,属…

蓝桥杯算法精讲:二分查找实战与变种解析

适合人群:蓝桥杯备考生 | 算法竞赛入门者 | 二分查找进阶学习者 目录 一、二分查找核心要点 1. 算法思想 2. 适用条件 3. 算法模板 二、蓝桥杯真题实战 例题:分巧克力(蓝桥杯2017省赛) 三、二分查找变种与技巧 1. 查找左边…

Thales靶机在网络安全教学与实战中的应用与价值

1.下载好该靶机,将桥接模式改为NET网段并启动 https://download.vulnhub.com/thales/Thales.zip 2.借助kali来确定该靶机的IP arp-scan -l nmap -O 192.168.56.101 3.访问改IP 192.168.56.101 4.点击manager app发现有个登录 使用msf爆破一下 msfconsole search…

docker 容器 php环境中安装gd 、mysql 等扩展

1、先配置阿里云镜像源 cd /etc/apt echo "" > sources.list echo "deb http://mirrors.aliyun.com/debian/ bullseye main contrib" >> /etc/apt/sources.list echo "deb-src http://mirrors.aliyun.com/debian/ bullseye main contrib&q…

springboot继承使用mybatis-plus举例相关配置,包括分页插件以及封装分页类

以下是使用 MyBatis-Plus 分页插件的完整配置和封装步骤&#xff0c;包括日志输出、驼峰转下划线、逻辑删除以及分页属性类的封装。 1. 引入依赖 确保在 pom.xml 中已经引入 MyBatis-Plus 的依赖&#xff1a; <XML> <dependency><groupId>com.baomidou<…

《Python深度学习》第七讲:生成式深度学习

在深度学习的世界里,生成式模型是一种非常有趣且富有创造力的技术。它们能够生成全新的内容,比如文本、图像、音乐等,甚至可以创造出从未见过的虚拟世界。这一讲,我们将深入探讨生成式深度学习的核心技术,包括 LSTM 文本生成、DeepDream、神经风格迁移、变分自编码器(VAE…

阿里开源的免费数据集成工具——DataX

企业里真实的数据流转是什么样子的呢&#xff1f; 左侧描述了一个企业真实的样子&#xff0c;我们总是需要把数据从一个地方搬到另一个地方&#xff0c;最后就是搬来搬去搬成了一张张解不开的网。 右侧则表达了使用DataX为中心实现数据的同步。 什么是DataX DataX是一个异构…

大数据Trino面试题及参考答案

目录 解释 Trino 的协调节点(Coordinator)与工作节点(Worker)的职责与交互流程 Trino 为何采用多阶段执行模型(Multi - stage Execution)?其优势是什么? 描述 Trino 查询从提交到结果返回的完整生命周期 Trino 的 “无共享”(Shared - Nothing)架构如何实现高并发…