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位空间冲突概率极低。
常见误解纠正:
- “ULID 可以完全替代 UUID。”——可以,但需注意 ULID 只有80位随机,碰撞概率略高于 UUID v4(2^80 vs 2^122),不过对于绝大多数实际系统(每秒生成数万 ID)仍然安全。
- “ULID 保证全球唯一时间顺序。”——不保证。同一毫秒内的顺序是随机的,且如果两台机器时钟不同步,时间戳可能颠倒。
- “ULID 必须大写。”——不,大小写不敏感且完全等价。输入时无论大小写都会被正确解析,但本生成器输出固定为大写字符(Crockford base32 标准格式提倡大写,但实现允许小写)。
- “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 包)来确保单调性。本工具为一次性生成工具,不做跨毫秒的单调性保证。