使用方法
- 「UUID」框默认已经填好 f47ac10b-58cc-11ec-9b8b-0242ac130003,改内容立刻重新解析;框里按 Ctrl+Enter(Mac 上 Cmd+Enter)可一次解析多行,每行一个 UUID。万一被清空,点解析时会自动填回这一行。
- 输入只认 8-4-4-4-12 这一种写法,共 36 个字符。带 urn:uuid: 前缀、带花括号、或字母大小写混写都能接受;缺字符、多了连字符、出现非十六进制字母会直接报出具体原因。
- 「碰撞概率样本量 n」默认 1000000,允许 1 到 1000000000000。它用来估算「生成 n 个 v4 的 UUID 至少撞一次」的概率,按生日界公式 n² ÷ (2 × 2^122) 计算;光标停在这个框里按回车同样触发一次。
- 还原出来的时刻一律按 UTC 显示,与 100 纳秒计数直接对应;想换算成北京时间请自行加 8 小时,本页不读浏览器时区,避免同一串字符在不同机器上得到不同结果。
- 结果区给出「UUID → version / variant / 时间」这一行,下面跟着 timeLow、timeMid、timeHiAndVersion、clockSeq、node 与 16 个字节的逐项清单。
- 清单下面是一张 12 行的字段树表,把每个字段的取值、位宽与来源逐条列出,默认显示 6 行,点「加载更多」展开全部,点「导出 CSV」存成带字节序标记的表格文件。
- 「复制结果」复制整段字段清单;「复制对照表」复制正文那张六个示例 UUID 的对照表,可直接粘进表格或文档。
- 输入为空、格式不是 8-4-4-4-12、样本量不是 1 到 1000000000000 之间的整数,都会给出针对该条的具体提示,不会拿上一次的结果冒充本次输出。
这个 UUID 版本解析器能解决什么问题
第一类是拿到一个 UUID 却不知道它是什么版本。版本号就写在第 13 个十六进制字符上,肉眼很难一眼定位:f47ac10b-58cc-11ec-9b8b-0242ac130003 的第 13 个字符是 1,所以它是 v1;018f8b7a-9c1d-7e2f-8a3b-6c5d4e3f2a1b 是 7,所以它是 v7。本页把这个字符连同它所在的字节一起标出来,不再靠猜。
第二类是从 v1 的 UUID 反推生成时刻。v1 的时间字段是自 1582-10-15 起的 100 纳秒计数,不是 Unix 时间戳,直接读会得到一个莫名其妙的巨大整数。本页减掉 122192928000000000 再除以 10000,把 f47ac10b-58cc-11ec-9b8b-0242ac130003 还原成 2021-12-09T08:49:43.990Z,可以直接拿去对日志时间。
第三类是排查 ID 能不能当数据库主键用。v4 是全随机,插入 B 树索引时页分裂频繁;v7 把 Unix 毫秒放在前 48 位,字典序约等于时间序,新增记录总是落在索引右端。本页标出版本,也就标出了这个 ID 在索引里的局部性。
第四类是发现隐私泄露。v1 的 node 字段当年就是主机的 MAC 地址,f47ac10b-58cc-11ec-9b8b-0242ac130003 的 node 是 02:42:ac:13:00:03,其中 02:42:ac 是 docker 网桥的常用前缀。把这类 UUID 发给外部,等于同时交出了主机的网卡标识与生成时刻。
这个工具在本站也叫「UUIDVersionExplainer」,两种叫法指的是同一件事,下面会讲清 version 与 variant 分别在哪个位置。
计算原理与变量说明
UUID 是 16 个字节,写成十六进制再插上四个连字符,一共 36 个字符。字段的划分按字节算:第 1 到 4 字节是 timeLow,第 5 到 6 字节是 timeMid,第 7 到 8 字节是 timeHiAndVersion,第 9 到 10 字节是 clockSeqHiAndReserved 加 clockSeqLow,第 11 到 16 字节是 node。
version 在第 7 字节的高 4 位。也就是第 13 个十六进制字符,取值 0 到 15。RFC 4122 定义了 1 到 5,后来的 RFC 9562 又加了 6、7、8。1 是时间序加节点,3 是 MD5(命名空间 + 名字),4 是随机,5 是 SHA-1(命名空间 + 名字),6 是把 v1 的时间字段重排成可排序形式,7 是 Unix 毫秒加随机。
variant 在第 9 字节的高 2 位。也就是第 17 个十六进制字符的高两位,取值只有四种:0xx 是 NCS 向后兼容,10x 是 RFC 4122(本页绝大多数 UUID 都属于这一类),110 是微软遗留,111 保留给将来。所以第 17 个字符只能是 8、9、a、b 之一才符合 RFC 4122;f47ac10b 这一段里的 9 正好落在 9b 上。
v1 的时间基准不是 Unix。它是自 1582-10-15 起的 100 纳秒计数,共 60 位:timeLow 占低 32 位,timeMid 占中间 16 位,timeHiAndVersion 去掉版本位后占高 12 位。要换算成 Unix 毫秒,先减 122192928000000000(两个基准之间的 100 纳秒个数),再除以 10000。f47ac10b-58cc-11ec-9b8b-0242ac130003 的计数是 138583325839900939,减完除完得到 1639039783990 毫秒。
为什么必须用 BigInt。60 位的最大值是 1152921504606846975,而 JS 的安全整数上限是 2^53 = 9007199254740992。用 Number 做「timeHi × 2^48」这一步时,乘积早已超过 2^53,低位的几位会被直接吃掉,还原出来的时间可以差出好几分钟。本页用 BigInt 做移位与减法,只在最后一步转回 Number 交给 Date。
v7 的前 48 位就是 Unix 毫秒。018f8b7a-9c1d-7e2f-8a3b-6c5d4e3f2a1b 的前 12 个十六进制字符 018f8b7a9c1d 等于 1716032019485,直接就是 2024-05-18T11:33:39.485Z,不需要任何基准换算。也正因为毫秒在高位,两个 v7 的 UUID 按字符串比较大小,结果与按生成时间比较一致。
v4 的随机数有多少位。128 位里 4 位是 version、2 位是 variant,剩下 122 位随机,组合数是 2^122 = 5.31691198314e+36。按生日界估算,生成 n 个时至少撞一次的概率约为 n² ÷ (2 × 2^122):n = 1000000 时约 9.404e-26,n = 1000000000 时约 9.404e-20,n = 1000000000000 时约 9.404e-14。
nil 与 max 是例外。全零的 00000000-0000-0000-0000-000000000000 的 version 是 0、variant 是 NCS;全一的 ffffffff-ffff-ffff-ffff-ffffffffffff 的 version 是 15、variant 是保留(未来)。两者都不符合 RFC 4122,本页照实标出,不把它们归进任何已定义版本。
完整示例
默认示例 f47ac10b-58cc-11ec-9b8b-0242ac130003:第 13 个字符是 1,version = 1;第 9 字节是 0x9b,高两位是 10,variant = RFC 4122。三个时间字段拼出的 60 位计数是 138583325839900939,减去 122192928000000000 得 16390397839900 939… 再除以 10000 得 1639039783990 毫秒,即 2021-12-09T08:49:43.990Z。clockSeq 取第 9 字节低 6 位与第 10 字节拼成 14 位,等于 7051;node 是后 6 个字节 02:42:ac:13:00:03。
换成 6ba7b811-9dad-11d1-80b4-00c04fd430c8:同样是 v1,计数 131059232331511825,减基准除 10000 得 886630433151 毫秒,即 1998-02-04T22:13:53.151Z,clockSeq 180,node 00:c0:4f:d4:30:c8。这是 RFC 4122 文档里那个经典示例,可以拿来交叉验证。
换成 9f1c5d3a-8b2e-4f1d-9c7a-1e2b3c4d5e6f:第 13 个字符是 4,version = 4,variant 仍是 RFC 4122,clockSeq 7290,node 1e:2b:3c:4d:5e:6f。v4 没有可用时间,本页不还原时刻,只给出 122 位随机位与组合数。
换成 018f8b7a-9c1d-7e2f-8a3b-6c5d4e3f2a1b:version = 7,前 48 位 1716032019485,直接得 2024-05-18T11:33:39.485Z,clockSeq 2619,node 6c:5d:4e:3f:2a:1b。再换成全零与全一那两个,分别得到 version 0 配 NCS、version 15 配保留(未来)。
UUID 字段与版本对照表
下表是六个示例 UUID 的解析结果,第三列给出该版本能还原出的时刻或随机位规模。
| UUID | version | variant | 还原结果 |
|---|---|---|---|
| f47ac10b-58cc-11ec-9b8b-0242ac130003 | 1 | RFC 4122 | 2021-12-09T08:49:43.990Z |
| 6ba7b811-9dad-11d1-80b4-00c04fd430c8 | 1 | RFC 4122 | 1998-02-04T22:13:53.151Z |
| 9f1c5d3a-8b2e-4f1d-9c7a-1e2b3c4d5e6f | 4 | RFC 4122 | 随机 122 位 |
| 018f8b7a-9c1d-7e2f-8a3b-6c5d4e3f2a1b | 7 | RFC 4122 | 2024-05-18T11:33:39.485Z |
| 00000000-0000-0000-0000-000000000000 | 0 | NCS | nil UUID |
| ffffffff-ffff-ffff-ffff-ffffffffffff | 15 | 保留(未来) | max UUID |
各版本的构造方式与容量,读的时候配合上面那张表一起看。
| 版本 | 构造方式 | 能否还原时间 |
|---|---|---|
| v1 | 时间序 + 节点(MAC) | 能,60 位 100ns 计数 |
| v3 | MD5(命名空间 + 名字) | 不能,由名字决定 |
| v4 | 随机 122 位 | 不能 |
| v5 | SHA-1(命名空间 + 名字) | 不能,由名字决定 |
| v6 | 重排时间序 | 能,字段重排后可排序 |
| v7 | Unix 毫秒 + 随机 | 能,前 48 位即毫秒 |
v4 的碰撞概率按生日界估算,n 增大两个数量级,概率增大四个数量级。
| 生成个数 n | 至少撞一次的概率 | 生成个数 n | 至少撞一次的概率 |
|---|---|---|---|
| 1000000 | 9.404e-26 | 1000000000 | 9.404e-20 |
| 10000000 | 9.404e-24 | 100000000000 | 9.404e-16 |
| 100000000 | 9.404e-22 | 1000000000000 | 9.404e-14 |
读第一张表看版本分布:六个示例覆盖 v1、v4、v7 与两个特殊值。读第二张表看能否还原时间:只有 v1、v6、v7 带时间。读第三张表看量级:n 每乘 10,概率乘 100。
结果边界、舍入与适用说明
- 输入为空时提示「输入为空,请粘贴一个 UUID,例如 f47ac10b-58cc-11ec-9b8b-0242ac130003」,不会拿上一次的结果冒充本次输出。
- 不是 8-4-4-4-12 结构时报出具体行号与内容,并说明十六进制字符总数应为 32、连字符应为 4 个。
- 出现 g 到 z 之外的字母时提示该字符不是十六进制数字,请检查有没有把 O 当成 0、把 L 当成 1。
- 样本量 n 为空、写成小数或超出 1 到 1000000000000 时,分别给出对应的具体提示,不自动取整也不取默认。
- v1 与 v6 的时间精度是 100 纳秒,但还原出的毫秒只保留三位小数;v7 的精度就是毫秒,没有更细的部分。
- v3 与 v5 由命名空间与名字哈希得出,不含时间也不含节点,本页只标版本,不尝试反推名字。
- 碰撞概率用生日界近似 n² ÷ (2 × 2^122),只在 n 远小于 2^61 时成立;本页给出的 n 上限远低于这个量级。
- 时刻一律按 UTC 输出,100 纳秒计数与 Unix 毫秒两个原始值也同时给出,方便你按自己的时区复核。
- 页面不联网、不写本地存储,UUID 与样本量只用于本次计算。
常见问题
version 和 variant 到底在第几个字符?为什么是 13 和 17?
UUID 去掉连字符是 32 个十六进制字符,两个字符一个字节。version 是第 7 字节的高 4 位,也就是第 13 个字符;variant 是第 9 字节的高 2 位,也就是第 17 个字符的高两位。连字符插在第 8、12、16、20 个字符之后,所以数字符时要跳过它们。
为什么 v1 的时间要减 122192928000000000?
因为 v1 的时间基准是 1582-10-15(格里高利历启用日)而不是 1970-01-01,单位是 100 纳秒。122192928000000000 就是这两个基准之间相差的 100 纳秒个数。减完得到的是自 Unix 基准起的 100 纳秒数,再除以 10000 才是毫秒。
为什么解析出来的时间会差几分钟?是算错了还是减错了?
多半是用 Number 而不是 BigInt 做移位。60 位的计数值最大到 1152921504606846975,远超 JS 安全整数 2^53,做「timeHi × 2^48」时低位被舍掉,误差可以达到数万毫秒。本页全程用 BigInt 计算,只在最后一步转回 Number。
v1 的 node 字段会泄露什么?
v1 的 node 原本就填主机网卡的 MAC 地址,等于同时暴露设备标识与生成时刻;f47ac10b-58cc-11ec-9b8b-0242ac130003 的 node 是 02:42:ac:13:00:03,前缀 02:42:ac 是容器网桥的常见段。RFC 4122 后来允许用随机数填充,v4 之后干脆整段随机,所以对外发布的 ID 不建议用 v1。
为什么有人说 v7 能当数据库主键而 v4 不行?
因为 v7 把 Unix 毫秒放在前 48 位,字符串比较的结果与生成时间顺序一致,新记录总是追加到索引右端,B 树的局部性好;v4 全随机,每次插入落在索引的任意位置,页分裂与写放大都更严重。本页标出版本,就是让你判断手上的 ID 属于哪一种。
nil 和 max 这两个特殊 UUID 算哪个版本?
都不算。全零的 nil UUID 的第 13 个字符是 0,第 9 字节高两位是 00,variant 归 NCS;全一的 max UUID 版本位是 15,variant 归保留(未来)。RFC 4122 定义的版本是 1 到 5,后来的 RFC 9562 扩到 8,15 不在其中,本页照实标出而不归类。
这个工具免费吗?
免费,无需注册即可使用。
输入的数据安全吗?
安全。解析在当前浏览器本地完成,不会上传输入内容。