qinghuan
← 回到知识探索

后端现场 / 趣味知识

订单编号该递增,还是用 UUID?

把生成方式、索引成本、排序需要和对外展示分开,才能比较编号方案。

展开本文目录
  1. 先说明在哪个范围里不能重复
  2. UUID 也有带时间顺序的版本
  3. 主键大小会影响其他索引
  4. 连续号码可以是另一项业务功能
  5. 记住这三点
  6. 别把例子用过头
  7. 自己试一小步
  8. 查原始资料
  9. 找一份资料继续练
开始前,会这些就够了
  • 知道主键用于识别一行
遇到不熟悉的词,先看这 2 个解释
索引
数据库为查找数据维护的辅助结构,让某些查询可以缩小要检查的范围,而不必总从头逐行寻找;新增、修改和删除数据时,相关索引也需要维护。
主键
表中用来唯一识别一行的一列或一组列,要求这组值不重复、各列也不为空;其他数据或程序可以借它准确指向那一行,而不只依赖可能重名的文字。
打开完整术语小抄 ↗

假设一个练习商城最初只有一台数据库,用递增数字当订单主键很方便。后来移动端希望离线建立草稿,几个系统又要合并数据,于是有人提议全部换成 UUID。这个决定不能只用“数字短”或“UUID 不重复”来拍板,因为编号同时参与生成、存储、查询和外部交互。

先说明在哪个范围里不能重复

单个数据库可以用序列或自增列协调分配数字;多个互不联系的生成者则需要约定各自范围,或使用适当的分布式生成方式。UUID 是一组一百二十八位标识格式,其中 v4 使用随机或伪随机位,通常不必为每次生成联系一个中央计数器。

“极难碰撞”和“数学上绝不碰撞”不是同一句话。随机源、实现错误和数据导入仍需要考虑,数据库唯一约束仍有价值。对一条重要写入,应明确遇到冲突怎样处理,而不是因为名字里有“唯一”,就取消最后一道数据约束。

UUID 也有带时间顺序的版本

RFC 9562 定义的 UUIDv7 在高位放入毫秒时间信息,并用其余部分承载随机数据或符合规则的补充字段。因此讨论“UUID 的插入顺序”时,要先问是哪一版,不能把 v4 的随机性套到所有 UUID 上。时间排序也不自动等于多个机器上的真实全局发生顺序。

假设甲先取得一个编号,却做了较长计算;乙后取得编号,先提交事务。按编号排出来,甲仍可能在乙前面,但提交顺序恰好相反。需要展示创建时间、支付时间或严格业务序号时,应明确记录相应事件,而不是指望一个主键回答所有时间问题。

主键大小会影响其他索引

在 MySQL 8.4 InnoDB 中,表的行数据通常按主键组织为聚簇索引,二级索引记录也携带主键。因此较长主键的空间成本可能出现在多个索引里。UUID 是一百二十八位,但把它存成二进制还是带连字符文本,数据库字段与编码也会影响实际占用。

递增与分散的键会让插入位置不同,但性能还取决于数据量、缓存、并发和存储结构。不能仅凭一个编号样例就宣布谁快十倍,更不能把 InnoDB 的结论原封不动搬到另一种数据库。评估时固定存储类型、索引数量和负载,再比较写入与查询成本。

连续号码可以是另一项业务功能

递增也不代表没有缺号。PostgreSQL 17 文档明确说明,事务回滚等情况不会收回已领取的序列值,因此不能拿普通序列承诺连续无缺号。若业务确实要求编号连续,需要单独设计分配、作废和审计规则,并接受相应协调成本。

内部主键与给用户看的订单号可以分开设计:一个服务于存储关联,一个服务于输入、查找或业务规则。随机编号也不能代替权限检查,知道一个编号不意味着有权读取对应订单。最后把需求写成清单:谁生成、能否离线、怎样合并、如何排序、冲突怎么办,再选择符合它们的方案。

记住这三点

  • 先界定唯一范围和生成者,再比较递增数字与 UUID。
  • UUIDv4 与 UUIDv7 的结构不同,不能统称为随机主键。
  • 编号大小、生成顺序、提交顺序和业务连续性是不同要求。

别把例子用过头

  • 随机碰撞概率低不等于无条件唯一,仍应处理约束冲突。
  • 本篇没有提供性能测评,索引影响需要在选定数据库中验证。
  • 主键不能自动充当权限凭据或连续业务序号。

自己试一小步

离线草稿需要本地生成 ID,正式订单又想用短内部主键,能否分别设计?

  1. 列出草稿同步到服务器的身份对应关系。
  2. 假设同一草稿重传两次,描述如何避免生成两笔正式订单。
  3. 说明对外订单号、内部主键与创建时间分别负责什么。

做到这些,就算完成

  • 没有把编号生成等同于重试去重。
  • 定义了合并和冲突规则。
  • 不要求一个字段同时保证连续、全局时间顺序和随机不可预测。

查原始资料

想确认一个细节,先看这份资料对应的部分,不必从头读完所有文档。

找一份资料继续练

先选一项,按下面的范围继续学;不必从头读完整门课程。