uuid ulid nanoid database distributed-systems backend

现代唯一标识符 (ID) 选择指南:超越 UUID v4

全面解析 UUID v1-v8、ULID、NanoID、CUID2 和 Snowflake ID。了解如何为您的数据库、分布式系统和 URL 安全选择正确的唯一标识符。

选择正确的唯一标识符 (ID) 是系统架构中最关键的决策之一。选择不当的 ID 可能会导致数据库性能瓶颈、安全漏洞或分布式系统同步问题。长期以来,UUID v4 一直是开发者的“默认”选择。然而,随着分布式系统和大规模数据库的发展,UUID v4 的局限性(如缺乏可排序性和 B-Tree 索引碎片化)变得越来越明显。

在本指南中,我们将深入探讨 UUID 的演变,研究 ULID 和 NanoID 等现代替代方案,并提供一个框架,帮助您根据具体用例选择最佳 ID。


1. UUID 的演变 (从 RFC 4122 到 RFC 9562)

通用唯一标识符 (UUID) 数十年来一直是行业标准。最近,IETF 通过 RFC 9562 更新了标准,引入了专门为现代数据库和分布式系统需求设计的新版本。

UUID v1:基于时间

UUID v1 结合了当前时间戳、时钟序列和生成器的 MAC 地址。

  • 优点:保证在空间和时间上的唯一性(如果 MAC 地址唯一)。
  • 缺点:隐私问题(暴露 MAC 地址),且如果在不同机器上生成,则不是单调递增的,这会损害 B-Tree 性能。

UUID v3 & v5:基于名称 (MD5/SHA-1)

这些是确定性的。如果您提供相同的命名空间和名称,您将得到相同的 UUID。

  • UUID v3:使用 MD5。
  • UUID v5:使用 SHA-1(优于 v3)。
  • 用例:当您需要基于唯一输入生成可复现的 ID 时(例如,从 URL 生成 ID)。

UUID v4:随机

最常见的版本,由 122 位随机位组成。

  • 优点:碰撞概率极低,不暴露敏感信息。
  • 缺点完全不可排序。当用作 B-Tree 索引(如 MySQL 的 InnoDB)的主键时,它会导致严重的“索引碎片”和“页拆分”,随着表的增长,性能会严重下降。

新一代:UUID v6, v7 和 v8

RFC 9562 引入了这些版本,旨在解决 v4 的排序问题,同时保持 UUID 格式。

UUID v7:新的金标准

UUID v7 是时间有序的。它使用 48 位 Unix 时间戳(毫秒精度),后跟随机位。

  • 为什么它很棒:它是按字典顺序可排序的。当用作数据库主键时,新记录被追加到 B-Tree 的末尾,从而最大限度地减少页拆分并保持高性能。
  • 建议:对于几乎所有新项目,UUID v7 应该取代 UUID v4 作为默认 ID。

UUID v6

基本上是重新排序的 UUID v1,使其可排序。仅当您对 UUID v1 结构有遗留依赖但需要排序功能时才使用它。

UUID v8

允许自定义实现,同时保持 UUID 结构。适用于需要在 128 位 ID 中嵌入特定元数据的专有系统。


2. 现代替代方案深度解析

虽然 UUID 是标准,但由于在可读性、长度或分布式生成方面的特定优势,几种替代方案也广受欢迎。

ULID (Universally Unique Lexicographically Sortable Identifier)

ULID 是 UUID v7 的强有力竞争者。

  • 结构:48 位时间戳 + 80 位随机数。
  • 编码:使用 Crockford 的 Base32,长度仅为 26 个字符(相比之下,标准 UUID 字符串为 36 个字符)。
  • 优点:URL 安全、不区分大小写、可排序,且视觉上比 UUID 更短。
  • 缺点:不是标准的 UUID 格式(某些旧数据库类型处理它的效率可能不如原生的 128 位 UUID/GUID 类型)。

NanoID:小巧快速的替代方案

NanoID 常用于前端和 Web 应用。

  • 优点:比 UUID 小得多,字母表高度可定制,比 UUID v4 更快。
  • 安全性:使用加密强度的随机生成器。
  • 用例:非常适合短链接生成器、需要简短且不可猜测字符串的面向公众的记录 ID。

CUID2:下一代安全 ID

CUID2 旨在实现安全性和水平扩展友好性。

  • 特性:极高的抗碰撞性、非连续性(防止枚举攻击),且可跨编程语言移植。
  • 用例:当安全性和水平扩展比严格的时间排序更重要时。

Snowflake ID:分布式重量级方案

Snowflake ID 最初由 Twitter 开发,是 64 位整数。

  • 结构:时间戳 + 工作机器 ID + 序列号。
  • 优点:生成速度极快,适合标准 BIGINT(64 位),且时间有序。
  • 缺点:需要集中或协调的“工作机器 ID”分配,以防止集群中的碰撞。

3. 对比表

特性 UUID v4 UUID v7 ULID NanoID Snowflake CUID2
长度 128 位 128 位 128 位 可变 64 位 可变
可排序性 是 (时间) 是 (时间) 是 (时间)
格式 十六进制 十六进制 Base32 字母数字 整数 字母数字
碰撞风险 微不足道 微不足道 极低 可配置 零 (若有协调) 极低
可读性 极佳
数据库原生支持 否 (二进制/字符串) 否 (字符串) 是 (BIGINT) 否 (字符串)

4. 选择 ID 的最佳实践

用于数据库主键

  • 如果您的数据库支持 128 位 UUID(PostgreSQL、现代 MySQL、SQL Server),请使用 UUID v7。它在唯一性和 B-Tree 性能之间提供了最佳平衡。
  • 如果您正在构建大规模分布式系统并希望通过 64 位整数节省空间,请使用 Snowflake ID
  • 避免在大表中使用 UUID v4;随机插入会摧毁您的写入性能。

用于公开 URL

  • 使用 NanoID 或 SQID。它们简短、URL 安全且美观。
  • 短 UUID(Base58 或 Base62 编码的 UUID)也是一个很好的选择,既保持了底层的唯一性,又提供了更简洁的 URL。

用于分布式系统 (微服务)

  • ULID 或 KSUID 是极好的选择,因为它们不需要中央协调器(与 Snowflake 不同),但仍然提供基于时间的排序,这对调试和日志记录非常有用。

5. 代码示例

在 Node.js 中生成 UUID v7

使用 uuid 包:

const { v7: uuidv7 } = require('uuid');
console.log(uuidv7()); // 例如 '018c3b7a-6b5d-7e8c-9a1b-2c3d4e5f6g7h'

在 Python 中生成 ULID

使用 python-ulid 库:

from ulid import ULID
ulid = ULID()
print(ulid) # 例如 '01H6P7XG6WJ9S5H8K4M6N7B2P1'

6. 常见陷阱 (FAQ)

问:我可以将现有的 UUID v4 转换为 UUID v7 吗? 答:不可以,位结构不同。但是,您可以开始为新记录使用 UUID v7。大多数数据库可以在同一个 UUID 列中存储两者。

问:NanoID 是否和 UUID 一样安全? 答:是的,前提是选择了正确的长度和字母表。21 个字符的 NanoID 与 UUID v4 具有相似的碰撞概率。

问:为什么不直接使用自增整数? 答:自增整数对于小型、单节点数据库非常有用。但是,它们会向竞争对手泄露您的数据量(例如 user/5000 告诉别人您有 5000 名用户),并且在分布式系统中难以管理。


7. 总结

在 2026 年,几乎没有理由在数据库主键上坚持使用 UUID v4。UUID v7 的兴起提供了一个标准化、时间有序的解决方案,在保持兼容性的同时解决了性能问题。对于特殊需求,ULID 提供了更好的可读性,而 Snowflake ID 在大规模分布式架构中提供了最高效率。

在确定 ID 方案之前,请评估您对可排序性、可读性和性能的需求。未来的您(以及您的数据库)会感谢现在的决定。