ULID 生成器

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

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

这个 ID 的结构

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

你的 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 包)来确保单调性。本工具为一次性生成工具,不做跨毫秒的单调性保证。