AZ Tools
← 指南

如何选择 ID 格式: UUID、ULID、Snowflake 还是自增

几乎每个系统都需要给事物分配唯一 ID,而这个选择会在很久之后才显现后果 — 体现在数据库性能上、ID 是否泄露信息上,以及不同服务在不冲突的前提下生成 ID 的难易上。本指南比较常见格式,给你一种有意识地选择、而非凭习惯选择的方法。

自增整数

经典的数据库 ID: 1、2、3 …… 由数据库分发。它们很小、索引很快,并天然按创建顺序排列。对于单一数据库,这往往就够了。

它们的弱点出现在边缘。只有中心数据库能铸造它们,因此独立服务或离线客户端无法提前生成 ID。它们还会泄露信息: 竞争对手可以读你的发票号来估算订单量,URL 中的连续 ID 也会诱使人猜测相邻记录。

UUID 版本 4(随机)

v4 UUID 是 122 个随机比特,通常写成 36 个字符。任何人、在任何地方都能在无需协调的情况下生成一个,碰撞概率实际为零,因此非常适合分布式系统、利于合并的键,以及不应被猜到的公开标识符。

代价是大小和排序。它们比整数大得多,而且因为完全随机,所以不按时间排序 — 把随机键插入典型的 B 树索引会使写入分散,在规模变大时可能损害数据库性能。它们也不携带内嵌信息,这有时是优点,有时是局限。

按时间排序的 ID: UUID v7、ULID 和 Snowflake

更新的一类通过把时间戳放在前导比特里来解决排序问题,于是后生成的 ID 排在先生成的之后,同时仍然全局唯一。UUID v7 在标准 UUID 格式内做到这一点。ULID 用一种紧凑的 26 字符、对 URL 友好、不区分大小写的编码做同样的事。Snowflake ID(由 Twitter 推广)把时间戳、机器 ID 和每毫秒计数器打包进一个 64 位整数。

时间排序在没有中心权威的情况下,给你利于索引的插入和免费的时间排序。代价是创建时间如今在 ID 中可见 — 通常无害,偶尔是你宁愿不暴露的东西。

这个选择实际影响什么

选择格式时,四个属性会一起变动,明确权衡它们会有帮助。

  • 协调: 任意节点都能独立生成 ID(UUID/ULID/Snowflake),还是只有中心数据库(自增)?
  • 排序: ID 能否按创建时间排序(自增、v7、ULID、Snowflake),还是不能(UUID v4)?
  • 大小与索引成本: 整数最小; 随机 UUID 最大、对索引最不友好。
  • 信息泄露: 连续整数暴露体量并诱发枚举; 随机 UUID 什么都不暴露; 按时间排序的 ID 暴露时间戳。

一个合理的默认选择

如果你只有一个数据库且不公开暴露 ID,自增整数简单又高效。如果 ID 是公开的、由许多服务生成,或用作 URL slug,就选用 UUID — 当你为数据库效率而希望它们大致按时间排序时选 v7 或 ULID,当你特别希望它们什么都不暴露时选 v4。

无论选什么,都要在一个系统内保持一致,避免对同一类实体混用格式。最糟糕的 ID 策略,往往是无意间选定的那一个。

相关工具