ULID 生成器

在线生成 ULID:26 字符 Crockford Base32,包含 48 位时间和 80 个随机位。

格式
生成结果
就绪,可在浏览器里生成 ULID。

这个 ID 的结构

结构
26 个 Crockford Base32 字符:前 10 个是时间,后 16 个是随机部分。
48 位毫秒时间戳之后,还有 80 个随机位。
时间
包含。前 10 个字符编码毫秒时间,字典序会跟随时间。
碰撞风险
随机尾部有 80 位;风险主要取决于同一毫秒内生成多少个 ID。
示例
01M12BRNYZKJQFCP0BHSZ8KNMG

你的 ID 会用浏览器里的强随机源本地生成,不会发送到 BroBroGo。

常见问题

ULID 适合什么场景?

ULID 紧凑、URL 友好,纯文本也能按时间排序,适合日志、对象键和需要按创建时间排列的记录。

ULID 和 UUID v7 一样吗?

不一样。两者都有毫秒时间,但 ULID 是 26 字符 Crockford Base32,UUID v7 保留标准 UUID 十六进制形态。

ULID 生成器的核心差异

与其他标识符生成页面不同,本页面向您提供的 ULID 格式在保持128位总长度的前提下,将表示长度压缩至26个字符(UUID 为36字符)。它只使用 Crockford 的 base32 字母表(数字0-9,字母A-Z但排除 I、L、O、U),因此生成的字符串大小写不敏感、不含连字符,并且天然 URL 安全——无需任何转义。这一设计使得 ULID 在“短暂易读”与“防冲突”之间找到了一个实用平衡点,尤其适合需要时间排序且对外暴露的场景。

ULID 的内部结构与编码原理

每个 ULID 的长度严格为26个字符,其底层128位数据被明确划分为两部分:

  • 高48位(前10个字符):表示从 Unix 纪元(1970-01-01T00:00:00.000Z)开始至今的毫秒数。由于采用 Crockford base32 编码,48位需要恰好10个字符(48 ÷ 5 = 9.6,向上取整至10,实际编码中多出的2位用0填充)。
  • 低80位(后16个字符):完全由加密级随机数填充。10个字符编码48位,剩余16个字符编码80位随机——这80位随机提供约2^80种可能。

这种编码方式使得 ULID 本质上是一个按创建时间排序的字符串:同一毫秒内生成的多个 ID,其前10字符完全相同,后16字符随机。但由于 base32 的字符集(0-9, A-Z 但不含 I, L, O, U)排除了容易混淆的字母(如 I 和 1,O 和 0),人工转录时出错概率大幅降低。同时,大小写不敏感意味着“ABCDEFGHIJKLMNOPQRSTUVWXYZ”与“abcdefghijklmnopqrstuvwxyz”被认为是相同字符,进一步简化了跨系统传输时的处理。

与 UUID v4 和 UUID v7 的对比

特性 ULID UUID v4 UUID v7
字符串长度 26 字符 36 字符(含4个连字符) 36 字符
大小写敏感性 不敏感(字母表固定) 通常不敏感但非强制 通常不敏感
时间排序 是(毫秒精度,前缀时间戳) 否(完全随机) 是(毫秒精度,但含版本位)
连字符 有(标准格式)
URL 安全性 天生安全 连字符需处理 连字符需处理
随机部分 80 位 122 位 74 位
碰撞概率 理论上2^80空间,极低 2^122空间,极低 2^74空间,较低

关键差异

  • UUID v4 随机空间更大,但完全不可排序,在数据库 B 树索引中易导致页分裂和随机 IO。
  • UUID v7 引入了时间排序,但保留连字符,长度与 UUID v4 相同,且随机部分缩小至74位。
  • ULID 去掉了连字符,长度压缩28%,同时保持128位总空间(注意:UUID 也是128位,只是用连字符和版本位占用了部分表示空间)。对于需要对外提供短标识符的系统(如 API 主键),ULID 是一个更紧凑的选择。

排序行为与时间戳限制

ULID 的排序完全依赖前10个字符的毫秒时间戳。由于编码是 lexicographical(字典序),且 Crockford base32 的字符顺序与二进制编码一致,所以字符排序就等于时间排序。例如:

01ARZ3NDEKTSV4RRFFQ69G5FAV  (2023-01-01T00:00:00.000Z)
01ARZ3NDEKTSV4RRFFQ69G5FAW  (同一毫秒,后16位不同)
01ARZ3NDEKTSV4RRFFQ69G5FAX

但在同一毫秒内生成的多个 ID,其时间戳部分完全相同,因此仅靠字符串比较无法区分先后顺序——它们之间是完全随机的。这意味着:如果您需要严格单调递增的 ID(例如在数据库分区中要求顺序写入),ULID 不能保证;但如果您只是想“新生成的 ID 大概率在后”以实现 B 树索引的局部性,那么 ULID 的毫秒级排序已经能显著降低插入时的页分裂概率。

实际使用建议:如果您的系统生成速率超过每秒1000个 ID,可以考虑在应用层手动添加单调性计数器(如每毫秒加1),但本生成器不提供此功能——它只生成独立、无状态的 ULID。

生成过程与隐私保护

所有 ULID 的生成都完全在您的浏览器中执行,无需向任何服务器发送数据。工具使用 crypto.getRandomValues()(现代浏览器提供的加密级随机数接口)填充80位随机后缀。这意味着:

  • 您的生成行为不会留下任何网络痕迹。
  • 即使需要生成100个 ID,也仅需一次浏览器 API 调用即可获得足够随机字节。
  • 生成的 ID 仅存在于当前页面的 DOM 中,刷新页面即丢失(除非您手动复制保存)。

当您点击“复制全部”时,页面上所有 ID 会被连接成一个文本块(通常每个 ID 一行)复制到剪贴板,随后显示“Copied all!”状态。复制单个 ID 时,只复制该字符本身,不包含换行或引号。

适用场景与常见误解

适合使用 ULID 的场景

  • 数据库主键:尤其适合 MySQL InnoDB 等 B+ 树引擎,因为时间排序可减少索引碎片。
  • 公开 API 标识符:比 UUID 短,视觉干净,无需处理大小写问题。
  • 日志系统:按时间排序的 ID 可帮助快速定位事件窗口。
  • 分布式系统:各节点独立生成,无需协调,128位空间冲突概率极低。

常见误解纠正

  1. “ULID 可以完全替代 UUID。”——可以,但需注意 ULID 只有80位随机,碰撞概率略高于 UUID v4(2^80 vs 2^122),不过对于绝大多数实际系统(每秒生成数万 ID)仍然安全。
  2. “ULID 保证全球唯一时间顺序。”——不保证。同一毫秒内的顺序是随机的,且如果两台机器时钟不同步,时间戳可能颠倒。
  3. “ULID 必须大写。”——不,大小写不敏感且完全等价。输入时无论大小写都会被正确解析,但本生成器输出固定为大写字符(Crockford base32 标准格式提倡大写,但实现允许小写)。
  4. “ULID 包含字母 I、L、O、U。”——不,Crockford base32 特意排除了这四个容易混淆的字母,因此 ULID 中只出现:0-9, A-H, J-K, M-N, P-T, V-W, X-Z。

常见问题 FAQ

1. ULID 的碰撞概率有多大?

ULID 的随机部分为80位,空间约为1.2×10^24。以每秒生成1000个 ID 计算,期望的首次碰撞时间远超宇宙年龄。在合理使用规模下(<10^9个 ID),碰撞概率可以忽略。

2. 为什么我生成的 ULID 前几个字符总是相同?

因为前10个字符是时间戳。如果您在很短时间内(短短几毫秒)连续多次点击“生成”,时间戳变化可能极小,导致前几位完全相同。这属于正常行为。如果希望获得完全不同的前缀,可以间隔几秒再生成。

3. 我可以控制 ULID 的大小写吗?

本页面上 ULID 生成器不提供大小写切换。由于 Crockford base32 对大小写不敏感,您复制后可以自己转换为小写(例如用于某些需要小写路径的 URL)。但需要知道:大小写不同但字母相同的两个字符串在语义上是等价的。

4. ULID 和 UUID v7 哪个更适合做数据库主键?

两者都按时间排序,但 ULID 更短(26 vs 36字符),且没有连字符,在存储和显示上略胜一筹。不过 UUID v7 是 IETF 标准(RFC 9562),而 ULID 是社区规范(非标准)。选择取决于您对标准化的需求程度。对于 MySQL 的 Bigint 主键,两者都需要以二进制形式存储(BINARY(16))才能发挥性能,字符串形式会降低索引效率。

5. 本生成器可以生成多少 ULID?可以生成超过100个吗?

本页面的计数输入范围限定为1到100。如果您需要更多,可以多次生成并合并结果。所有生成均发生在客户端,没有服务器限制。

6. 生成的 ULID 会重复吗?

理论上,在2^80的随机空间中,同一毫秒内几乎不可能重复。但如果您在同一毫秒内生成大量 ID(例如在测试中快速点击),由于浏览器随机数的质量和对 crypto.getRandomValues() 的调用方式,极端情况下存在极低概率的重复。生产环境建议使用专门的库(如 ulid npm 包)来确保单调性。本工具为一次性生成工具,不做跨毫秒的单调性保证。