← 返回题目列表

分布式 ID 要不要带业务含义?

高频 中等 第 2 / 25 题 更新于 2026/10/04
分布式ID业务编码安全性可演进性
学习 AI 实战项目

简化版

内部主键通常不应编码地区、类型、日期等业务含义,因为规则会变化,编码会泄露信息并把解析逻辑耦合到主键。 推荐用无业务含义的稳定 ID 做关联,另设可变业务字段;若确需可读订单号,则把它作为独立外部编号,并明确版本、校验位和不可作为权威业务事实的边界。

详细版

“ID 带含义”初期方便人工识别,但地区合并、业务改类、数据迁移后,旧编码无法自然更新,更新又会破坏外键与幂等。把时间或序号暴露给外部还会泄露单量,并便于遍历接口。

数据库主键使用 Snowflake、号段或 UUID 等 opaque ID;订单展示号可由 版本+渠道码+日期+随机/序列+校验位 生成,并单独建立唯一索引。业务查询基于正式字段,不依赖字符串切片。外部编号需要防猜测时加入足够随机性或映射 token。

完整版教学

一、主键和业务编号职责不同

主键负责长期唯一、稳定引用和数据库性能;业务编号负责人可读、客服沟通或渠道对接。把两者合成一个字段,会让展示规则变化牵动所有关联表。内部 ID 一旦生成应终身不变,业务编号可以按版本演进。

记忆钩子:主键用来“认对象”,业务字段用来“描述对象”;描述会变,身份不应变。

二、业务含义为什么会过期

订单号前两位表示省份,后来用户跨区迁移;第三位表示业务类型,后来类型拆分;日期按创建时间编码,但补单要求使用原业务日。编码时的事实不一定永远成立,解析结果会逐渐成为历史快照,却常被误当成当前真值。

编码内容初期收益长期风险
地区码人工识别迁移、合并后失真
业务类型快速分类类型演进困难
日期便于排序暴露单量、时区歧义
顺序号可读可枚举与热点

三、耦合会怎样扩散

一旦报表、路由、分库规则和权限都通过 substring(id) 获取含义,修改编码需要同时升级许多系统。新旧版本并存时,某些消费者甚至会把新编号解析成错误业务。正式字段有 Schema、索引和迁移流程,隐藏编码通常没有。

错误:region = orderId.substring(0, 2)
正确:region = order.regionCode  // 有明确版本与迁移规则

四、安全与隐私问题

20260930-00001234 暴露日期内序号,攻击者可估算业务规模并遍历相邻订单。即使接口有鉴权,可预测 ID 也会增加越权漏洞利用效率。外部 ID 可以使用 96~128 位随机 token、带密钥的不可逆映射,或在顺序号上增加随机段。

不可把“ID 很难猜”当作鉴权;每次访问仍要校验资源归属。隐藏规模只是降低信息泄露和扫描便利度。

五、确需可读编号时如何设计

把内部 order_id 与外部 order_no 分开,后者带显式 format_version。例如 A2-20260930-7K4M9P2DX6Q8TN5R3WYC-H 中 A2 是格式版本/渠道组合,中间 20 位 Base32 随机段提供约 100 bit 随机空间,H 是输入错误校验位。数据库仍要对 order_no 建唯一索引,极低概率碰撞时重新生成;随机性只降低枚举风险,不能替代鉴权。

业务日期、地区仍保存在独立列,编号只用于展示与查找。格式升级时旧编号继续作为字符串,不要求重新编码。

六、长度、索引和迁移

长字符串主键会扩大 B+Tree 与所有二级索引;因此内部 ID 可保留 64 位整数,外部编号仅在需要的表上索引。若 1 亿行表的二级索引都携带 32 字节字符串,相比 8 字节整数会显著增加缓存和 I/O。

迁移旧业务 ID 时先增加新内部列、回填映射、双读验证,再切外键;不能原地重写所有主键。监控编号碰撞、生成失败、格式版本与外部枚举攻击。

七、常见误区与追问

  • 误区:ID 带日期就能省去创建时间字段。 时区、补单和格式变化都会让语义不可靠。
  • 误区:业务含义能减少一次数据库查询。 省掉查询却增加跨系统耦合和错误解析。
  • 误区:随机 ID 就不需要鉴权。 不可猜测性不能替代资源级授权。
  • 误区:外部订单号必须同时做数据库主键。 两者可以独立并以唯一索引关联。
  • 追问:客服需要识别渠道怎么办? 展示独立渠道字段或版本化业务号,不从内部主键推断。
  • 追问:含时间的 Snowflake 算业务含义吗? 它含生成机制信息,但不应被业务逻辑当作权威创建时间。
  • 追问:如何防外部遍历? 随机 token/映射加严格鉴权、限流和审计。

八、加强记忆

分布式 ID 设计遵循“身份稳定、描述独立、外部不可猜、格式可版本化”。内部主键保持 opaque,地区、类型和日期用正式字段表达;需要可读编号就另建唯一外部号,并把解析限制在展示层。这样业务变化不会迫使全库改身份。

即使当前编码规则看似稳定,也应把 ID 当不透明字符串跨服务传递;消费者自行截位解析会形成无法集中升级的隐式协议。

真正需要携带语义的场景,应定义有版本、校验位和兼容策略的业务编码,并与数据库主键的生命周期分离。