在分布式系统和数据库设计中,“如何生成一个唯一的ID” 一直是个经典话题。我们最熟悉的可能是 UUID,但它也有不少槽点:无法排序、存储占用大、可读性差。今天给大家介绍一个非常有潜力的替代品 —— ULID(Universally Unique Lexicographically Sortable Identifier)。

什么是 ULID?

ULID 的全称是 通用唯一词典分类标识符。简单来说,它是一种既能保证全局唯一,又天然支持按时间排序 的标识符。

它的设计目标很明确:

  • 全局唯一:不怕分布式环境下的冲突
  • 时间有序:生成顺序 = 时间顺序
  • 紧凑可读:比 UUID 更短、更友好

ULID 长什么样?

一个标准的 ULID 看起来是这样的:

01H4Z7X8J7F9VYMQK7Z9G9Z9Z9

它是基于 Crockford's Base32 编码的(包含 0–9A–Z,去除了 ILOU 以避免混淆),生成的字符串通常统一显示为大写,解码时大小写不敏感。

ULID 的结构

ULID 一共 128 位(16 字节),分为两部分:

部分 长度 作用
时间戳 48 位 Unix 毫秒时间(1970–2088)
随机数 80 位 保证唯一性
┌─────────────── Timestamp (48 bits) ───────────────┐
┌───────────────────────────────────────────────────┐
│  Time  │                Randomness                │
└───────────────────────────────────────────────────┘

为什么要选 ULID?

1、全局唯一性

ULID 结合了时间 + 随机数,即使在高并发的分布式环境中,碰撞概率也极低。

2、天生支持排序

因为时间戳在最前面,ULID 天然按时间递增。你可以直观地看到,随着时间推移,前面的字符会变大:

01H4Z...(较早)

01H5A...(较晚)

这意味着:

  • 数据库按主键排序 ≈ 按时间排序
  • 不需要额外的时间字段
  • 查询更快,索引更高效

Tip

不要试图在 Windows 11 的资源管理器里按文件名排序来验证 ULID 文件名的时间排序,因为该资源管理器目前只支持自然排序。可以尝试换其他资源管理器或在命令行中列出文件。

3、比 UUID 更紧凑

类型 长度 示例
UUID 36 字符 550e8400-e29b-41d4-a716-446655440000
ULID 26 字符 01H4Z7X8J7F9VYMQK7Z9G9Z9Z9
  • 更短

  • 不含 -

  • 更适合 URL / 文件名

4、高性能 & 去中心化

ULID 的生成:

  • 不需要中心服务器
  • 不需要锁
  • 本地即可生成,非常适合微服务、消息队列、日志系统

5、跨平台支持

主流语言几乎都有成熟实现:Java / Go / Python / Rust / JavaScript。

什么时候不该用 ULID?

虽然 ULID 很香,但并不是万能药。以下场景请慎重考虑:

  • ❌ 需要完全随机且无规律的 ID 如果你的业务要求 ID 不能泄露任何时间信息(例如用于订单号防止被爬虫推测销量),ULID 的前缀时间戳可能会暴露生成频率和时间。这种情况下,UUIDv4 这种完全随机的方案更安全。
  • ❌ 需要绝对连续且无空洞的 ID ULID 是“趋势递增”,不是“绝对连续”。由于随机部分的存在,它会有空洞。如果你的系统要求 ID 必须严格连续(1, 2, 3...),ULID 不适合。
  • ❌ 存储空间极度敏感 虽然比 UUID 短,但如果是海量数据且不需要排序,单纯的整型自增 ID(Int/Long)仍然是最省空间的。

ULID 适合用在哪些场景?

  • 日志系统

    按时间生成、天然有序,非常适合做日志追踪 ID。

  • 分布式系统

    不依赖数据库自增 ID,避免单点瓶颈。

  • 数据库主键

    B+Tree 索引友好,写入性能更好。

  • 消息队列

    消息 ID 自带时间顺序,消费端更容易处理。

总结

ULID 是一个兼顾“唯一性、时间排序、紧凑性”的现代分布式 ID 方案。如果你正在寻找一个比 UUID 更优雅、比雪花算法更简单的方案,不妨试试 ULID。