Skip to content

GUID 到底是什么?浅析 GUID 的结构、版本与唯一性

2026-05-24

标签:Windows · GUID · 科普 · QuickGUID


如果你在 Windows 上做过开发,你一定见过它——一串长得像乱码的十六进制字符:

6B29FC40-CA47-1067-B31D-00DD010662DA

它可能出现在注册表里、项目配置文件里、日志输出里,或者某个报错信息的中间。你知道它叫 GUID,也知道它"应该是唯一的",但你可能从没认真想过——这串东西到底是什么?它为什么长这样?它凭什么不会重复?

今天我们就来聊聊这件事。

UUID 和 GUID:同一件事的两个名字

先说一个很多人不知道的事实:GUID 和 UUID 是同一个东西。

  • UUID(Universally Unique Identifier,通用唯一标识符)是正式名称,定义在 RFC 4122 中。
  • GUID(Globally Unique Identifier,全局唯一标识符)是微软给它取的别名。

就这么简单。UUID 是标准名,GUID 是微软在 COM、Windows API、.NET 等技术栈中使用的名字。你在 Windows 上看到的 GUID 和你在 Linux 上看到的 UUID,本质上是一回事。

为了统一,本文后面主要用 GUID 这个名字——毕竟我们聊的是 Windows 开发。

GUID 的结构:看着乱,其实有规律

一个标准 GUID 长这样:

6B29FC40-CA47-1067-B31D-00DD010662DA

8-4-4-4-12,总共 32 个十六进制字符(128 个二进制位),用连字符分成 5 段。看着随机,但其实是有结构的:

6B29FC40-CA47-1067-B31D-00DD010662DA
              ↑    ↑
             版本  变体

关键在第三段的第一个字符和第四段的第一个字符:

  • 版本号(Version):第三段的第一个十六进制位表示这个 GUID 是用什么算法生成的。1 表示 v1,4 表示 v4,7 表示 v7。
  • 变体(Variant):第四段的第一个十六进制位表示它遵循的是哪个标准规范。89AB 开头表示遵循 RFC 4122——你日常见到的绝大多数 GUID 都是这个变体。

所以下次看到一串 GUID,只要瞄一眼第三段的首位,就能知道它是 v1、v4 还是 v7 生成的。这比看外边有用得多。

GUID 到底有哪几个版本?

UUID 规范定义了多个版本,但实际在用的主要是三个:

v1:基于时间和 MAC 地址

v1 GUID 由当前时间戳(精确到 100 纳秒)和网卡的 MAC 地址组合生成。

优点:天然有序,按生成时间排列。缺点:暴露了生成时间,而且 MAC 地址是硬件信息——这在隐私和安全上是硬伤。攻击者拿到 v1 GUID 就能反推出你的网卡地址和生成时间。

所以现在很少有人用 v1 了,但你在老旧系统中还是能见到它的身影。

v4:纯随机

v4 是目前最常见的版本。除了版本号和变体位是固定的,其余 122 个二进制位全部由随机数填充

没有时间戳,没有硬件信息,纯靠随机性保证唯一。简单粗暴,但效果极好。

你的项目中 new Guid()uuid.uuid4()crypto.randomUUID() 生成的几乎都是 v4。

v7:时间排序的新贵

v7 是 2024 年才正式纳入标准的新版本。它把前 48 位用来存毫秒级时间戳,剩下的位用随机数填充。

为什么要搞 v7?因为 v4 虽然好,但它完全随机,没法按时间排序。这对数据库来说是个问题:

  • 数据库用 GUID 做主键时,如果用 v4,因为完全随机,插入顺序和索引顺序不一致,会导致 B+ 树频繁分裂,写入性能下降。
  • v7 因为前半部分是时间戳,天然按时间递增,插入顺序和索引顺序基本一致,写入性能好得多。

所以如果你的项目用 GUID 做数据库主键,v7 是比 v4 更好的选择。

"GUID 不会重复"——但网上那个段子算错了

每次说到 GUID 唯一,总有人问:万一重复了怎么办?

网上流传着一个说法:"v4 GUID 的碰撞概率小到可以忽略不计,每秒生成 10 亿个,持续 10 亿年也不会重复。"听起来很安心,对不对?

但这个说法在数学上其实是错的。

错在哪?生日悖论

它掉进了一个经典的直觉陷阱——生日悖论

你可能听过这个问题:一个房间里需要多少人,才能让"其中两个人同一天生日"的概率超过 50%?直觉告诉你,一年 365 天,怎么也得 180 个人吧?

实际上,只需要 23 个人就够了。

原因在于,我们不是在看"某个人和某个特定日子"会不会撞,而是看"所有人两两之间"会不会撞。人数一多,两两组合的数量急剧膨胀。

GUID 也是一样的道理。我们不能只看"某一个 GUID 会不会碰巧等于另一个",而是要看"已经生成的所有 GUID 之间,任意两个会不会撞车"。组合数会随总量呈平方级增长,远比直觉告诉你的快得多。

真实的碰撞时间线

如果严格按照每秒 10 亿个的速度不停歇地生下去,用生日悖论公式重新算一遍:

  • 10 年:碰撞概率约 1%
  • 33 年:碰撞概率约 10%
  • 86 年:碰撞概率约 50%

至于 10 亿年?那时候碰撞的次数已经数不过来了。

那还能放心用吗?

当然能。上面的前提是"每秒 10 亿个,不停歇"——现实中没有任何应用能做到这个量级。

如果你整个系统的生命周期里总共生成了 100 万亿个 GUID(这已经是极其夸张的天文数字),碰撞概率也只有十亿分之一

放心用吧。

日常开发中的 GUID 烦恼

了解了原理之后,回到实际。开发者日常和 GUID 打交道,真正的痛点不在于"它会不会重复",而在于:

生成不方便

Windows 自带的 guidgen.exe 一次只能生成一个。在终端里?PowerShell 倒是可以:[guid]::NewGuid()。但如果你需要一次生成 50 个做测试数据呢?写脚本吧。

格式五花八门

同一个 GUID,在不同语言和场景下长得很不一样:

6B29FC40-CA47-1067-B31D-00DD010662DA          // 标准格式
6B29FC40CA471067B31D00DD010662DA              // 无连字符
{6B29FC40-CA47-1067-B31D-00DD010662DA}        // 花括号(C#/COM)
urn:uuid:6B29FC40-CA47-1067-B31D-00DD010662DA // URN
aKXwnELGRYeysdADQClibto=                      // Base64

你从日志里复制一个 GUID,需要转成 C 代码里的 DEFINE_GUID 宏格式?手动改吧。

从文本中提取很麻烦

日志文件里散落着几十个 GUID,你想把他们都找出来?肉眼扫描还是写正则?

regex
[0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[0-9a-fA-F]{4}-[0-9a-fA-F]{4}-[0-9a-fA-F]{12}

能用,但每次都要查一次才能写对。

用 QuickGUID 解决这些问题

这些痛点其实都可以在一个工具里解决。QuickGUID 是一款 Windows 原生的 GUID 工具箱:

  • 批量生成 v4 或 v7,最多一次 1,000 个
  • 10+ 种格式实时转换:标准、无连字符、花括号、Base64、C 字节数组、DEFINE_GUID 宏……全部实时预览
  • 智能提取:粘贴日志或源码,自动识别所有 GUID 并批量转换
  • 实时装饰器:引号、后缀、大小写,全局切换,复制出来直接能用

完全免费,100% 离线。

写在最后

GUID 是软件开发中最不起眼但最可靠的基础设施之一。128 个二进制位,不需要中央协调,不需要联网,就能在全球范围内保证唯一性。这个设计从诞生到现在已经超过 30 年,依然是分布式系统中标识符的最佳实践之一。

希望这篇文章帮你搞懂了 GUID 背后的那些"为什么"。