UUID 生成器

在浏览器中生成 UUID:完全随机的 v4,用于数据库键的按时间排序的 v7,或基于命名空间和名称的确定性 v5。一次最多生成 1000 个,数据绝不离开您的设备。

点击生成以抽选
版本
高级选项

需要一个不会与其他人创建的标识符发生冲突,在一个您从未听说过的机器上,且无需向任何人请求许可的标识符吗?这就是 UUID 的作用。它是一个 128 位数字,通常以熟悉的 8-4-4-4-12 格式写成 36 个字符,其整个设计的核心就是即使没有任何机制协调谁获取哪个,也不会有两个相同的 UUID。这个免费生成器在您的浏览器中生成它们:在“版本”中选择一个版本,在“生成数量”中说明您需要多少个,然后按“生成”。页面默认打开的是绝大多数项目所需的单个 v4,因此只需点击一下即可获得第一个标识符。

每个标识符都由您的浏览器通过 Web Crypto API 构建。版本 4 在浏览器支持的情况下使用 crypto.randomUUID(),否则使用 crypto.getRandomValues()——这是一个加密级别强度的随机源,而不是像 Math.random() 这样的 JavaScript 伪随机函数。版本 7 将 48 位的毫秒时间戳放在前面,接着是同一毫秒内递增的 12 位计数器,然后是 62 个随机位,这是 RFC 9562 中描述的排序方法;那个计数器就是为什么一次生成一千个批次会按顺序输出而不是打乱的原因。版本 1 将时间戳与设置了多播位的随机节点标识符结合在一起,因此它永远不会像 1990 年代的原始实现那样携带您的网卡地址。版本 3 和 5 完全不随机:它们使用 MD5 和 SHA-1 分别将一个命名空间与一个名称一起进行散列,并且这种散列处理同样是在您的设备上发生的。没有任何数据被发送到服务器,没有任何记录,而且一旦页面加载完毕,即使断开连接它也会继续工作。

“版本”是决定其他一切的字段。版本 4 是 122 个随机位并且没有结构——这是默认设置,也是当标识符只需要具有唯一性时的正确答案。版本 7 保留了 62 个随机位,但以它的创建时间开头,因此一组 v7 可以按其生成顺序进行排序;正是这一点使得它成为当标识符将用作数据库键时的首选版本。版本 1 是原始的基于时间的方案,保留在这里以供仍要求使用它的系统使用。版本 3 和 5 是特殊的一对:它们是确定性的,这意味着相同的输入总是产生相同的标识符,并且它们需要两个额外的字段——“命名空间”(标准空间 DNS、URL、OID 和 X.500 之一,或您自己的“自定义”空间)和“名称”(被标识的字符串)。为 v5 提供 URL 命名空间和 https://example.com/a,今天、明天以及在别人的机器上您都会得到相同的 UUID。正因为如此,当选择 v3 或 v5 时,“生成数量”会消失:一千份确定性值的副本仍然只是一千份完全相同的副本。对于其他每个版本,“生成数量”可以从 1 到 1000。

四个开关改变了输出的形状,而没有改变底层的 128 位。“大写”将十六进制数字打印为大写字母,“无连字符”提供了某些列和文件名偏好的 32 字符紧凑形式,而“大括号”将值包裹在 { } 中——这是 Windows 和 .NET 世界称为 GUID 的形式。在“高级选项”下,urn:uuid: 添加了使其成为正式 URN 的前缀;当它开启时,会关闭“大括号”和“无连字符”,因为标准只允许一种确切的 URN 形式,那就是带有连字符的规范形式。“复制格式”决定了获取批次时的连接方式:每行一个、以逗号分隔,或者每个值带引号并以逗号结尾,以便直接粘贴到代码的数组中。结果下方有一行报告位数的随机性——v4 为 122 位,v7 为 62 位——并且对于 v7,还会报告批次中第一个标识符内编码的时间戳,因此值内部的时间是可见的,而不是隐含的。“全部复制”和“保存为文件”都遵循“复制格式”的设置。“历史记录”在您的浏览器中保留了您最近的 10 个批次:点击一个即可将其调回,复制或删除单个条目,或清空全部。“清除”会清空结果但保留您的设置,当标识符超过 50 个时,列表会变成一个可滚动的块而不是五十行。

这个页面上最重要的一点就是 v4 和 v7 作为主键的区别,而且这并不是一个品味问题。数据库索引是按排序顺序保持的 B-tree,新键在顺序中落入的位置决定了插入操作的代价。v4 键是随机的,因此连续的插入落在索引中互不相关的角落:已满的页面被拆分,数据库保存在内存中的树的部分很少是下一次插入所需的部分,最终索引变得比其指向的行所对应的规模更大且更碎片化。v7 键以当前毫秒开始,因此连续的插入一起落在树的右边缘,这是自增整数产生的模式,也是索引结构围绕其构建的模式。这就是全部的权衡:v7 为您带来了顺序键的插入行为,同时保留了让您最初选择 UUID 的属性,即任何人都可以在不请求服务器的情况下铸造一个。它的代价是创建时间现在在标识符内部是可读的,而这是否要紧是关于您的数据的问题,而不是关于格式的问题。

没有任何东西会检查 UUID 的唯一性;其尺寸就是保证。这里没有注册表,没有服务器往返,没有查找——生成器产生一个数字并交给您,而其中两个不发生碰撞的原因仅仅是因为有 2^122 种可能的 v4 值,大约为 5.3 × 10^36。生日问题(birthday problem)是衡量它的最诚实方式:您需要大约 2.7 × 10^18 个标识符,才能有 50% 的几率使其中任何两个匹配,这相当于在 86 年里每秒生成十亿个新 UUID。精确了解这承诺了什么和没承诺什么是有价值的。这是一种统计上的保证,而非算术上的,并且它仅在底层的随机性是真实的时候才成立——一个播种不佳的生成器,或一个与其熵池一起被克隆的虚拟机,会以格式无法检测的方式打破这种保证。版本 7 进一步缩小了这个问题:在单个毫秒内,一个生成器根本不会重复自身,因为计数器是递增的而不是掷骰子,而在独立的生成器之间,62 个随机位发挥着作用。

UUID 是标识符,而不是密码。版本 4 是不可预测的,这诱使人们将其视为秘密——密码重置链接、不可猜测的 URL、会话令牌、API 键。不可预测性是真实的,但保密性是值被处理方式的一种属性,而标识符在设计上就是被漫不经心地处理的:它们存在于 URL 中,进入浏览器历史记录、服务器日志、来源标头和分析中,并被粘贴到聊天消息中。其他版本甚至是更糟糕的选择。版本 1 和版本 7 都编码了创建的时间,因此任何持有一个的人都能大致知道它是何时生成的,并能缩小与之一起生成的其他事物的范围。版本 3 和 5 故意是确定性的:一个电子邮件地址的 v5 UUID 不是一个被散列的秘密,它是一个任何拥有相同地址的人都可以在一秒钟内计算出的值。使用 UUID 来命名一个事物。当值必须保持未知时,请使用密码生成器,或为秘密构建的库中的令牌。

为什么要选择这个 UUID 生成器?

UUID 出现的场景,通常是在任何东西能够确认名字空闲之前,某物必须被命名的时刻。应用程序代码为即将插入的行铸造它们,以便对象在到达数据库之前就具有标识,并且客户端可以在写入操作仍在进行时引用它。分布式系统更加依赖它们:多个服务同时写入一个表,离线的移动客户端在飞机上创建记录,一个必须识别其已处理消息的队列——所有这些都不能等待共享计数器轮到自己。版本 5 满足了不同的需求,那是一个从您已经拥有的东西衍生出的稳定标识符:同一文档、同一 URL 或同一账户总是映射到相同的 UUID,所以一次导入可以运行两次而不复制任何内容。在其他地方,它们仅仅是系统要求的格式——贯穿日志和追踪的相关性标识符,当一千个用户的上传进入同一个存储桶时不会发生冲突的文件名,一个 Windows 注册表键,一个必须看起来像真实数据的测试夹具。

几个诚实的限制值得了解。一个批次代表一种版本:“版本”中的值适用于批次中的所有内容,因此一次 v4 生成和一次 v7 生成是两个不同的批次。这是一个生成器,而不是检查器——将一个 UUID 粘贴进来以回读它的版本或时间戳并不是本页的功能。“保存为文件”写入纯文本文件,而不是 CSV 或 JSON。版本 7 公开揭示它被创建的毫秒级时间,这是一种真正的权衡而不是缺陷,版本 1 以同样的方式泄漏时间;如果这对您的数据是个问题,版本 4 是那个对谁都不透露任何信息的版本。标准中有两个特殊的值不是版本,所以它们不是“版本”中的选项,但您可以从这里复制它们:nil UUID 即 00000000-0000-0000-0000-000000000000,以及 max UUID 即 ffffffff-ffff-ffff-ffff-ffffffffffff。这个页面上的一切都遵循 RFC 9562,这是 2024 年取代 RFC 4122 的规范,所以输出被任何处理 UUID 的库、数据库或 API 接受。在这些限制内,其保证是强有力的:每个标识符都是按照标准、从密码学安全的随机性中、在您自己的机器上构建的,而且没有其他人看到过它。

FAQ

我如何生成一个 UUID?

在“版本”中选择一个版本,在“生成数量”中设置您需要多少个,然后按“生成”。页面默认打开一个版本 4 标识符,这也是大多数项目所需的,所以一个普通的 UUID 离您只有一次点击之遥。每个值都配有自己的复制按钮,而“全部复制”可以一次性获取整个批次。

UUID v4 和 v7 之间的区别是什么?

版本 4 是 122 个随机位,完全没有结构,所以其中任何两个都没有相互关系。版本 7 将前 48 位用于创建的毫秒时间,增加了一个计数器和 62 个随机位,并且因此是可排序的:一个批次会按照它们被生成的顺序输出。当标识符只需要具有唯一性时使用 v4,而当它也将成为数据库键时使用 v7。

我应该使用哪个 UUID 版本?

除非您有理由选择其他版本,否则选择版本 4。如果标识符将成为主键且插入性能很重要,请选择版本 7;如果您需要相同的输入总是产生相同的标识符,请选择版本 5;只有当您需要交互的系统明确要求时,才选择版本 1。版本 3 是使用 MD5 而不是 SHA-1 的版本 5,为了兼容性而存在。

生成的 UUID 真的是随机且安全的吗?

随机性是密码学安全的:它来自您浏览器的 Web Crypto API,通过 crypto.randomUUID() 或 crypto.getRandomValues() 生成,而不是来自 Math.random()。在不可猜测意义上的安全与在保密意义上的安全是两个不同的问题——UUID 是一个标识符,它不应该被用作密码、会话令牌或 API 键。

我可以一次生成大量 UUID 吗?

可以,一次最多可生成 1000 个。设置“生成数量”并按“生成”;当值超过五十个时,结果会变成一个可滚动的单个区域,而不是很长的行列表。“全部复制”和“保存为文件”会获取整个批次,并按照“复制格式”指定的格式连接——每行一个,用逗号分隔,或者带上引号以便粘贴到数组中。

为什么 v3 和 v5 每次都给我相同的 UUID?

因为那正是它们的用途。版本 3 和 5 是确定性的:它们将您给定的命名空间和名称进行散列处理,因此相同的输入总是产生相同的标识符,无论在任何机器或任何语言中。这使它们在从您已有的信息衍生稳定标识符时非常有用,这也正是为什么选择它们时“生成数量”会被隐藏的原因。

什么是命名空间和名称,我应该选择哪个命名空间?

它们是计算版本 3 或版本 5 UUID 的两个输入。“命名空间”表明名称属于什么类型——DNS 用于主机名,URL 用于网址,OID 和 X.500 用于相应的目录方案,或者如果您想提供一个您自己的命名空间 UUID 可以选择“自定义”,这也是应用程序内部的常见选择。“名称”就是字符串本身。不同的命名空间对同一名称会给出完全不同的结果,这就是设置它们的意义。

为什么对于数据库主键,UUID v7 比 v4 更好?

因为值在索引中落入的位置不同。索引按排序顺序保持,而随机的 v4 键会落在其中的任何地方,因此插入操作会在整棵树上拆分页面,并且保存在内存中的部分很少是下一次所需的。v7 键以当前毫秒开始,因此连续的插入会一起落在索引的末尾,这正是一个顺序整数键的行为方式——同时仍然允许任何服务在不请求中央计数器的情况下铸造一个标识符。

两个 UUID 会有可能相同吗?

理论上是的,实际上不会,而了解哪种情况适用是有价值的。没有任何机制验证唯一性:仅仅是因为有 2^122 种可能的版本 4 值(大约 5.3 × 10^36),您需要大约 2.7 × 10^18 个标识符——在 86 年里每秒十亿个——才会使发生哪怕一次碰撞的机会达到 50%。该保证是统计上的而不是绝对的,它依赖于随机性的真实性,这也是为什么这个生成器使用 Web Crypto API 而非普通伪随机函数的原因。

我可以使用 UUID 作为密码、API 键或会话令牌吗?

不能,这是代价最高昂的错误。版本 4 UUID 是不可预测的,但标识符就像被当作公开信息一样处理:它们最终会出现在 URL、浏览器历史记录、服务器日志、来源标头和分析中。版本 1 和 7 额外揭示了它们的创建时间,而任何知道输入的人都可以重新计算版本 3 和 5。对于任何必须保密的东西,请使用密码生成器或专用于秘密的库提供的令牌。

UUID v7 会暴露它的创建时间吗?

是的,精确到毫秒,这是出于设计而非疏忽——时间戳正是使 v7 可排序的原因。结果下方的行向您展示了批次中第一个值编码的时间,因此您可以确切看到暴露了什么内容。版本 1 也带有时间戳。如果创建时间是您不想公开的,版本 4 是完全不包含时间的版本。

UUID v1 会暴露我的 MAC 地址吗?

在这里不会。原始方案将机器的网卡地址作为节点字段,这也是 v1 名声的由来,但标准同样允许一个设置了多播位的随机节点标识符,这就是这个生成器所产生的。您的硬件地址永远不会被读取,也永远不会出现在输出中。

大写、无连字符、大括号和 urn:uuid: 会改变什么?

仅仅是展现形式——底层的 128 位在每种形式中都是相同的。“大写”将十六进制数字打印为大写字母,“无连字符”提供紧凑的 32 字符版本,“大括号”用 { } 将值包裹起来,就像 Windows 和 .NET 书写 GUID 的方式一样,而 urn:uuid: 添加了使其成为正式 URN 的前缀。urn:uuid: 开启时会关闭其他两项,因为标准只定义了一种 URN 形式,那就是带连字符的规范形式。