简体中文

开发者工具 · UUID 生成器

ULID、Snowflake、KSUID 和 UUIDv7:可排序 ID 比较

· 背景

uuid 密码学 浏览器 API

四个并排的标识符布局:ULID、Snowflake、KSUID 和 UUIDv7,显示时间戳和随机性部分
原始 ToolAcre 矢量图

随机 UUID 不按创建时间排序,因此多种格式将时间戳放在前面。这篇文章在布局、大小、单调性和兼容性方面比较了 ULID、Snowflake、KSUID 和 UUIDv7。

随机 ID 和讨厌它们的索引 — 时间排序标识符解决的问题

当新记录到达时,随机 v4 UUID 会在 B 树主键上分散插入点,从而导致页面拆分和重组。在随机位置插入会降低写入性能并显着增加磁盘碎片。高吞吐量数据库可以容忍这种成本——真正独立、不协调的标识符的价格——但成本是真实的。如果您需要 UUID 按创建时间排序,则可以通过添加时间戳前缀来显着改善索引特性。已经出现了多种格式:ULID、Snowflake、KSUID 和 RFC 9562 v7。每个都在大小(26 字符到 128 位)、时间戳精度(秒到纳秒)、UUID 兼容性以及 ID 生成器协调是否需要集中化方面做出不同的权衡。数据库基准测试显示插入性能显着提高。

ULID — 48 位毫秒时间戳加上 26 Crockford base32 字符中的 80 随机位,具有单调选项

ULID(通用唯一字典顺序可排序标识符)将 48 位毫秒时间戳和 80 位随机负载编码为 Crockford base32 的 26 字符。文本表示按字典顺序正确排序,使 ULID 适合时间戳排序和可读性很重要的系统 - 日志处理、分布式跟踪、标识符需要在面向人类的输出中轻松读取的微服务。 ULID 提供了一种单调变体,其中在同一毫秒内生成的多个标识符会增加随机部分而不是重复,从而确保即使是快速的 ID 突发也能保持严格的生成顺序。代价是 ULID 不是 UUID:它不适合标准 128 位 UUID 数据库列,无需进行编码转换。 ULID 精度覆盖大约 8925 年。

Snowflake — 64 位 ID,来自时间戳、工作 ID 和序列,以及它们所需的协调

Snowflake 是一个 64 位标识符,最初由 Twitter 设计,结构为 41 位毫秒时间戳、10 位工作 ID 和 12 位序列号。 41 位时间戳覆盖大约 69 年,并在 2106 中溢出,需要纪元协调和迁移规划。 Worker ID 区分不同服务器或进程生成的标识符——每个 Snowflake 生成器必须知道自己唯一的 Worker ID,且不能与其他生成器发生冲突。 Snowflake 是 64 位,而不是 128,使其大小只有 UUID 的一半,索引速度更快,并且每个标识符的存储效率更高。它按时间和工作人员 ID 排序,对于按源路由请求或日志很有用。缺点是操作性的:必须为每个发电机分配一个工作 ID,时钟必须保持同步。

KSUID — 具有大随机负载的秒时间戳,按字节排序

KSUID(K-可排序唯一标识符)是一个 128 位标识符,由 32 位 Unix 秒时间戳和 96 位随机负载组成,通常编码为 27 base62 字符。该格式可按字典顺序排序,并且随机部分的大小在加密上是合理的。 KSUID 的采用不如 ULID 或 Snowflake 广泛,但提供了不同的语义:时间戳很容易解码为人类可读的秒(在日志和调试中有用),并且 96 位随机部分足够大,使得同一秒中生成的多个 KSUID 在没有序列协调的情况下实际上具有零重复概率。与 Snowflake 不同,KSUID 不需要工作 ID 协调或集中分配。 KSUID 以秒而不是毫秒为单位进行操作,因此一秒内的多个 ID 会随机排序,除非您实现额外的逻辑。

UUIDv7 — 适合现有 uuid 列和工具的标准跟踪答案

RFC 9562 v7 是一个 128 位标识符,由 48 位 Unix 毫秒时间戳、12 位亚毫秒精度(可用作序列计数器)和 62 随机位全部组合而成。它在数据库中作为字典字符串和 128 位字节正确排序。至关重要的是,它是一个有效的 UUID — 它将版本半字节设置为 7,并将变体位设置为 RFC 9562 标准,使其与处理 UUID 的每个工具、数据库列和 API 兼容。不需要编码转换,现有的 UUID 基础设施不需要修改。如果在同一毫秒内生成多个 v7 标识符,RFC 9562 建议使用亚毫秒字段作为单调计数器而不是随机位。 V7 代表了维护 UUID 兼容性的务实选择。

一毫秒内的单调性 - 每种格式如何处理突发以及为什么它对于排序保证很重要

单调性是指如果两个事件按可观察的顺序发生,则它们的 ID 以相同的顺序进行比较的属性。在现代硬件的毫秒级粒度上,多个事件通常会在同一时钟周期内发生,因此任何可排序的 ID 方案都必须正确处理亚毫秒排序。 ULID 提供显式单调模式,其中随机部分递增而不是随机化。 Snowflake 包含一个 12 位序列号,该序列号在一毫秒内递增。 KSUID 缺乏内置机制,因此除非添加额外的逻辑,否则亚秒级事件会随机排序。 RFC 9562 v7 建议使用亚毫秒字段作为单调计数器。如果您的系统每秒生成数千个 UUID,则一毫秒内的单调性会显着影响查询顺序。

这不包括 - 吞吐量基准,这取决于硬件和语言;帖子保持质量

不包括吞吐量基准和性能数据,因为它们严重依赖于硬件架构、语言实现、数据库引擎和缓存策略。根据您是否测量随机插入、范围查询、索引开销或实际生产负载下的总吞吐量,数据库性能特征会有很大差异。这篇文章保持定性,根据其设计从概念上比较格式,而不是提供可能产生误导的特定环境的数字。实际性能评估需要在您自己的环境中使用您自己的工作负载、代码库和操作约束进行测试。对不同 ID 格式进行基准测试是一项很有价值的练习。

要点:兼容性通常决定 — ToolAcre 生成器生成随机 UUID;使用其格式正确的检查来确认您的库中的 UUIDv7 解析为 UUID

兼容性通常决定选择哪种格式。如果您的数据库架构已经需要 UUID 列,则 v7 是在不离开 UUID 生态系统的情况下实现可排序性的现代答案。如果使用自定义 ID 类型构建新系统,ULID 可以提供更小的文本表示形式和毫秒精度优势。如果您需要 64 位存储并且可以通过集中分配来管理工作 ID 协调,那么 Snowflake 是大容量系统中经过验证的选择。基本的权衡是在标准兼容性(选择 v7)和替代属性(例如较小的尺寸(Snowflake)或 base32 可读性(ULID))之间。根据系统约束和生态系统决策做出选择。