
原来没看官方源代码前,我网上先搜了一下,做足了功课。一般都说 CLSID 的结构定义如下:
typedef struct _GUID {
DWORD Data1; // 随机数
WORD Data2; // 和时间相关
WORD Data3; // 和时间相关
BYTE Data4[8]; // 和网卡 MAC 相关
} GUID;
这看起来很合理,里面除了随机数,即有时间戳保证时间不重复,又有网卡保证物理区域不重复。
微软有专门生成 GUID 的 API, 叫 CoCreateGuid 给程序员调用。我看了代码,底层调用了 UuidCreate()。精彩部分的来了,查了 github 上的微软泄漏 UuidCreate()源代码后,发现整个函数核心就一句:
RpcStatus = GenerateRandomNumber((unsigned char *)Uuid, 16);
是的你没看错,就是生成 16 个随机数给用户。什么时间和网卡,全部都不存在!
是否碰撞?那就听天由命吧。只要冲突概率最小,那就可以忽略。(比如 TCP 包校验也有记录冲突,但同样选择忽略,具体可看这个贴: t/767293 )
1 Jirajine 2021-05-14 17:09:44 +08:00 via Android 泄露的是上古版本,简陋也正常。可能现在已经改了? |
2 3dwelcome OP @Jirajine 没改,微软的 VS 安装后,会自带一个 Create GUID 生成工具。 我一直天真的以为,这工具是 time base,或者和 Windows 系统时钟有点关系,然而今天发现,就是一个随机数生成器。 那我还不如自己程序来生产随机数呢。完全不加时间相关的变量的 GUID,大规模用起来,心里没底。 |
3 0x2CA 2021-05-14 17:22:57 +08:00 GUID 生成单线程生成用事件戳相同的的时间戳计数+1 就好,这样可以保证自己这个机子生成的唯一性 |
4 xupefei 2021-05-14 17:32:22 +08:00 via iPhone Version 4 UUID 就是全部随机数,你看到的带网卡地址的版本是二十年前的了 |
5 nannanziyu 2021-05-14 17:41:12 +08:00 |
6 xupefei 2021-05-14 17:42:11 +08:00 via iPhone 仔细看了看 lz 的贴,不知从何吐槽。 你又不知道 GenerateRandomNumber 是通过什么当种子的,怎么能说冲突概率大? |
7 3dwelcome OP @xupefei 时间戳算法是完全 0 冲突,另一个随机算法冲突概率极小,但小归小,冲突也是客观存在的吧。 只要是个严谨的码农,想都不用想,肯定选完全 0 冲突的算法。 生成慢点无所谓,关键就是在未来某年某月,千万别冲突就好。 @nannanziyu 不去深究,普通码农哪里会知道那么多。 就算安全性 MAC 去掉可以理解,但不能把时间戳也一起去掉吧? 我们以前同事写各种 COM 代码,都是直接那 Create GUID 工具来生成的。也是对微软无比信任,生成界面就算加个可选时间戳参数,或者加一段随机算法说明,那也是极好的。 |
8 nannanziyu 2021-05-14 17:59:56 +08:00 @3dwelcome 重新定义普通码农 你质疑一个 API 之前,至少要看下这个 API 自己的文档吧 https://docs.microsoft.com/en-us/windows/win32/api/rpcdce/nf-rpcdce-uuidcreate UuidCreate 的文档页就说了这个事情 |
9 3dwelcome OP @nannanziyu 你错了,普通码农只会调用 CoCreateGuid,这才是最常用的。没人会直接调用底层 RPC 的函数,也就是我提到了,你才会去查一下。 你去看看 CoCreateGuid 那个微软 API 文档,就没提到任何有关算法的事情。 |
10 kop1989 2021-05-14 18:13:53 +08:00 @3dwelcome #8 没太明白你想表达什么,调用的逻辑不就是 New GUID 》 CoCreateGuid 》 UuidCreate 么…… CoCreateGuid 的文档中也明确写着: The CoCreateGuid function calls the RPC function UuidCreate, which creates a GUID, a globally unique 128-bit integer. Use CoCreateGuid when you need an absolutely unique number that you will use as a persistent identifier in a distributed environment.To a very high degree of certainty, this function returns a unique value no other invocation, on the same or any other system (networked or not), should return the same value. |
11 3dwelcome OP @kop1989 我的控诉,是要微软给的 GUID 的唯一性得到算法上的保障!而不是返回几个随机数敷衍一下。 普通码农获取 GUID 的两个办法: 1. 用 VS 带的图形界面生成工具。 2. 用 CoCreateGuid 。 这两个方法,都不能在算法上得到保证。API 文档写了“Use CoCreateGuid when you need an absolutely unique number”, 所以我应该无脑信任微软,返回的 GUID 不重复吗? 你加一个 CoCreateGuidEx 函数都好啊,可惜什么都没有。 |
12 kop1989 |
13 SoloCompany 2021-05-14 18:32:22 +08:00 via iPhone rss 跳进来,发现有点不知所云,然后发现原来是 block 了楼主,解开看了下赶紧关掉,民科好可怕 |
14 agagega 2021-05-14 18:35:29 +08:00 via iPhone 这个就是 uuid v4 |
15 lovecy 2021-05-14 18:38:54 +08:00 没搞过 C++不知道微软 API 代码怎样 但是凡事一扯上绝对,那个代价基本就是无法承受的。所以人们都得在合理的范围内去靠近绝对,比如根据预估损失来决定安全程度。 所以其实就算发生了重复,也没楼主想象的那么糟 - - |
17 zhujinliang 2021-05-14 18:41:45 +08:00 via iPhone 首先,电脑时钟精度、同时产生 GUID 个数、不同电脑时间不同步等,导致时间戳可能相同,甚至不同网卡的 MAC 也可能会相同,不存在你说的 0 冲突 随机数算法肯定是密码学的随机数算法,理论上可保证随机性,否则全世界的加密算法的安全性就打折扣了 有非纯随机部分构成的 GUID,不过是其中一部分用一些其他算法得出的数值代替了随机数,很难说这些算法随机性上优于密码学随机数生成器 |
18 wevsty 2021-05-14 18:48:31 +08:00 一个定长的字节块事实上没有任何方法能保证 100%不重复,只能去想办法降低不重复的概率而已。 事实上就算加上时间戳也不能保证 100%不重复,而且要制造一个时间戳成本远低于要随机出相同长度的字节数。 |
19 3dwelcome OP @lovecy "其实代码里对这种极小概率重复做一下处理,代价是很低的" 微软自己内核用的是完全另外一套算法,用到的是高精度时钟,源代码我都找到了。 算法还读取注册表里防重复的值:HKEY_LOCAL_MACHINE\SOFTWARE\Microsoft\Rpc\UuidSequenceNumber 但问题是不公开,对普通程序员就区分对待,这点我就觉得很不爽。 |
20 AstroProfundis 2021-05-14 18:54:54 +08:00 谁给你说的时间戳算法是 0 冲突的让他请你吃饭 |
21 3dwelcome OP @kop1989 "GUID 类并没有承诺 GUID 一定是 100%不重复的。" 你去 wiki 查一下 guid, 有不是随机数,基于时间戳的版本(微软内部用的版本,比如早期 Office 的 COM GUID,都是这个生成的)。 原理就是写入 100ns 为间隔的时间戳,只要大于这个时间段生成的 GUID,就可以保证时间上无冲突,另外机器 ID 或者网卡 MAC,能保证空间上无冲突。 只要时空都能保证,那么就是 100%无冲突版本的 GUID 。也就是微软一开始用 GUID 算法的版本。 |
23 Mutoo 2021-05-14 19:04:44 +08:00 GUID 在同一个 business domian 里面重复的概率非常低,基本上可以忽略。不过全世界那么多 business domain,纯在相同的 GUID 是必然的。 但是我写的 APP 里面有一个跟你写的 APP 里有一个相同的 GUID 又如何?这是两个风马牛不相及的业务,并不会真正「冲突」。所以你要是担心重复,可以给你的业务逻辑再加细分子域就可以了。 |
24 EPr2hh6LADQWqRVH 2021-05-14 19:06:57 +08:00 只能说你对指数没有敬畏,2^128 什么概念缺少感性认识 |
25 3dwelcome OP |
26 3dwelcome OP @avastms 你买云服务器,可靠性 100%和可靠性 99.9999%,就是本质上的区别。 数的就是小数点后面,有几个 9 。 |
27 Mutoo 2021-05-14 19:11:48 +08:00 @3dwelcome 这个只能算是一个微软操作系统上的一个 business domain 。 全球开发者写再多软件,2^128 也够保证微软在有生之年使用了。而你软件业务上的其它 GUID 并不会跟这个 domain 冲突。 |
28 Aoang 2021-05-14 19:18:18 +08:00 via Android 用过 UUID 的都知道 v4 冲突的概率比 v1 小的多。 v4 一共有 2^128 个 UUID,而 v1 在同一时间同一机器才留了几位给随机数。 只要时间机器相同,v1 就是渣渣,这也是为什么没人用的原因。同一机器多个进程就傻了。 另,因为要用时间戳,v1 只会越用越少。 |
29 EPr2hh6LADQWqRVH 2021-05-14 19:19:04 +08:00 |
30 hoyixi 2021-05-14 19:22:27 +08:00 代码烂很正常,winxp 之前蓝屏司空见惯,谁的 win 里面还不养几种病毒当宠物 |
31 skies457 2021-05-14 19:31:18 +08:00 @3dwelcome "原理就是写入 100ns 为间隔的时间戳,只要大于这个时间段生成的 GUID,就可以保证时间上无冲突" -> 连续生成几个就懵逼了吧 |
32 3dwelcome OP @skies457 微软没那么傻,我看了代码,针对 100ns 额外处理有很多,也有随机数来保障。 比如预生成很多 guid 后,系统 cache 起来的。你代码获取到的 guid,时间戳有可能是早期的。 |
33 NXzCH8fP20468ML5 2021-05-14 19:43:22 +08:00 @3dwelcome 时间确实不会倒流,但不代表你电脑上的时间就正确啊。 |
34 3dwelcome OP @xxfye “但不代表你电脑上的时间就正确啊。” 不同电脑就代表着空域不同,GUID 最后面 8 个 BYTE,就是用来保证空间不一致的。 有个 SSL 设计我想顺便提一下,人家的 random 就不是单纯的随机数,是包含时间戳的。时间是很重要的变量,在一定程度上,要比随机数可控。 ![]() |
35 0bit 2021-05-14 20:18:43 +08:00 就是 UUID v1 和 v4 之争 呗,实际使用中一直用 v4,没用过 v1 。 |
36 geelaw 2021-05-14 20:56:09 +08:00 via iPhone 谁说时间不会倒流的 Windows 有 NTP 时间校准机制,很容易发生时间倒流的情况。当然要确保有生之年生成真不重复的 GUID 也很容易,人工控制时间戳、使用被毁灭的网卡的 MAC 地址并用 v1 算法即可。 毁灭网卡的例子: https://devblogs.microsoft.com/oldnewthing/20040211-00/?p=40663 |
37 3dwelcome OP |
38 billlee 2021-05-14 22:13:16 +08:00 MAC 地址也才 48 bits, 现在虚拟机这么多,MAC 也无法保证唯一。所以还不如整个 128 bits 的随机数让冲突概率小一点 |
39 billlee 2021-05-14 22:20:42 +08:00 至于 TCP 那个问题,我觉得应该理解为是当时的历史问题。那个时代 CPU 性能太差,而链路层一般都有强校验,所以 TCP 不太需要强校验了 |
40 lance6716 2021-05-15 00:47:19 +08:00 via Android 不知道楼主有没有用 git,git 也是会有几率遇到哈希冲突的哦,难不难受 |
41 mxT52CRuqR6o5 2021-05-15 01:04:31 +08:00 via Android 工程实践中 rsa 算法使用的素数也未必是真正的素数,难受不难受 |
42 Mutoo 2021-05-15 06:57:25 +08:00 via iPhone 0.1+0.2=30000000000000004 难不难受 |
43 swulling 2021-05-15 07:28:31 +08:00 via iPhone uuid4 碰撞的概率太低了。 这么说吧,每秒产出十亿个 uuid,连续产出 85 年,才有百分之五十的概率存在一次碰撞。 工程界不需要百分之百的保障 |
44 swulling 2021-05-15 07:30:22 +08:00 via iPhone 这么多 uuid,光 uuid 自己就有几十 EB,现在绝大部分实际的数据库有这么大么?担心什么碰撞?真是杞人忧天! |
45 sillydaddy 2021-05-15 10:14:15 +08:00 是的。 就是#43 楼的 @swulling 提到的概率。 如果觉得 50%概率太高,可以换一个说法:每秒产出千万个 uuid,连续产出 85 年,才有百万分之五十的概率存在一次碰撞。如果换成每秒百万个 uuid,那么 85 年产生一次碰撞的概率就是一亿分之五十。 典型的“生日问题”:( https://zh.wikipedia.org/wiki/生日 ),里面有概率的计算公式。 |
46 Te11UA 2021-05-15 11:00:47 +08:00 这几天 LZ 发的帖子真的令人大开眼界 |
47 g00001 2021-05-15 11:57:51 +08:00 几乎每天都能看到这些:Windows 多么差劲,用 Windows 难道不浑身难受,用 Widows 惊掉下巴 …… 一个产品能做到天天被人讨论也真是难得,虽然 Windows 占领了桌面系统最大的市场,但为什么总有人喷 Windows 呢?!因为这些喷 Windows 的人都是精英 精英们总是会有高人一等的看法,瞧不起不上档次的人间凡品 |
49 hst001 2021-05-15 12:57:57 +08:00 |
50 3dwelcome OP |
51 newmlp 2021-05-15 13:45:18 +08:00 随机有啥问题吗 |
52 3dwelcome OP @newmlp 随机当然没问题,有问题的是微软内部用的不是随机算法( v1 版本),给我们程序员用的是随机算法( v4 版本)。 明显不公平啊。我就想生成 Office Excel 那种简洁明了的 GUID,而不是随机乱码那种。 |
54 3dwelcome OP |
55 liuhan907 2021-05-15 18:02:33 +08:00 via Android @3dwelcome 所以你怎么就一定认为 ntp 返回时间就不会重复了呢。同时发生在两个机器上的请求怎么就不可能了? |
56 3dwelcome OP @sillydaddy 讨论概率的前提,是随机性足够高。这点对于这个案例不适用。 1. GUID v4 不是每个 128bit 都是变化的,不能满打满算。 2. GUID v4 里,GenerateRandomNumber 用到伪随机数生产算法,是 RC4 。 我不知道这算法每个 BIT 的概率是不是都相同的,不相同就不能用生日问题的概率来套用。( ps: 最常见的 LCG 随机数生成器,就是 vc 早期附带的 rand,每个 bit 就是不同的)。 在 wiki 上,RC4 也陆续被别的 ChaCha20/AES 安全算法给替代( https://en.wikipedia.org/wiki/RC4#RC4-based_random_number_generators)。当然不是说微软的 RC4 不好,一切数据都需要实测为准。 |
58 hst001 2021-05-16 23:42:33 +08:00 via Android 大家承认楼主是对的然后赶紧散了吧 |