ulid/spec: 全局唯一标识符ULID是传统UUID的替代


ULID 是 UUID 的替代品。它是可排序的并且基于时间戳+随机种子。有多种语言的实现可用。

Shopify从UUID切换到ULID,INSERT提升50%,以下是他们经验:

分布式系统使用不可靠的网络,即使网络在大多数时间看起来可靠。在 Shopify 的规模下,在支付处理过程中发生不可靠事件的概率为百万分之一,这意味着它每天会发生多次。如果这是超时的支付 API 调用,我们希望重试该请求,但这样做是安全的。对客户的卡重复收费不仅对持卡人来说很烦人,如果他们没有注意到重复收费并退款,它还会为商家打开潜在的拒付风险。双倍退款对商家的生意也不利。

简而言之,我们希望付款或退款只发生一次,尽管偶尔会出现问题,这可能会导致多次发送 API 请求。我们的集中支付服务可以通过发送对每个请求唯一的幂等密钥来跟踪尝试,其中至少包含一个或多个(重试的)相同的 API 请求。幂等键查找尝试完成的步骤(例如创建交易的本地数据库记录)并确保我们只向我们的金融合作伙伴发送一个请求。如果这些步骤中的任何一个失败并且收到具有相同幂等性密钥的重试请求,则会运行恢复步骤以在继续之前重新创建相同的状态。使用幂等性构建弹性 GraphQL APIs更详细地描述了我们的幂等性机制是如何工作的。

对于我们希望请求可重试的时间(通常为 24 小时或更短),幂等密钥需要是唯一的。我们更喜欢为这些幂等键使用通用唯一字典排序标识符 ( ULID ),而不是随机版本UUID。

ULID 包含一个 48 位时间戳,后跟 80 位随机数据​​。时间戳允许对 ULID 进行排序,这与用于索引的 b 树数据结构数据库一起工作得更好。

在 Shopify 的一个高吞吐量系统中,我们发现通过将幂等键从 UUIDv4 切换到 ULID,INSERT 语句持续时间减少了 50%

对于许多用例,UUID 可能不是最佳选择,因为:

  • 它不是编码 128 位随机性的最有效字符方式
  • UUID v1/v2 在许多环境中不切实际,因为它需要访问唯一、稳定的 MAC 地址
  • UUID v3/v5 需要唯一的种子并生成随机分布的 ID,这会导致许多数据结构中出现碎片
  • UUID v4 除了随机性之外不提供其他信息,这会导致许多数据结构中的碎片

相反,这里建议使用 ULID:

ulid() // 01ARZ3NDEKTSV4RRFFQ69G5FAV

  • 与 UUID 的 128 位兼容性
  • 每毫秒 1.21e+24 个唯一的 ULID
  • 字典排序!
  • 规范编码为 26 个字符的字符串,而不是 36 个字符的 UUID
  • 使用 Crockford 的 base32 以获得更好的效率和可读性(每个字符 5 位)
  • 不区分大小写
  • 无特殊字符(URL 安全)
  • 单调排序顺序(正确检测和处理相同的毫秒数)

项目点击标题