UUID会不会重复?概率多大?

哈希珊瑚
• 阅读 421

UUID会不会重复?概率多大?
公众号:疾风追马

前言

有一次面试,面试官问我 UUID 会不会重复,我当时只知道 UUID 是通用唯一识别码,既然是唯一,那肯定就不会重复呀。事实证明当前的我还是太年轻了,今天就来一次讲清楚 UUID 的知识。

正文

什么是 UUID?

通用唯一识别码(英语:Universally Unique Identifier,缩写:UUID)是用于计算机体系中以识别信息的一个 128 位标识符。

UUID 按照标准方法生成时,在实际应用中具有唯一性,且不依赖中央机构的注册和分配。UUID 重复的概率接近零,可以忽略不计。

因此,所有人都可以自行建立和使用 UUID,而且几乎可以确定其不会与既有的标识符重复。也因为如此,在不同地方产生的 UUID 可以使用于同一个数据库或同一个频道中,而且几乎不可能重复。

UUID 的应用相当普遍,许多计算平台都提供了对于生成和解析 UUID 的支持。
这是百科里关于 UUID 的描述,大多数人对于 UUID 的理解可能也就到这里了,但是为什么是重复概率接近零,而不是绝对不会重复呢?

UUID 的版本

UUID 目前标准版中有 5 个版本,这里版本的意思,其实就是生成规则,不同版本生成规则不同。

版本 1 的 UUID 是根据时间和节点 ID(通常是 MAC 地址)生成;

版本 2 的 UUID 是根据标识符(通常是组或用户 ID)、时间和节点 ID 生成;

版本 3、版本 5 透过对命名空间(namespace)标识符和名称进行散列生成确定性的 UUID;

版本 4 的 UUID 则使用随机性或伪随机性生成。

如何识别 UUID 的版本

UUID会不会重复?概率多大?

这是版本 4 的 UUID,第二个-后第一位就是他的版本号啦~

Java 中的 UUID

    /**
     * Static factory to retrieve a type 4 (pseudo randomly generated) UUID.
     *
     * The {@code UUID} is generated using a cryptographically strong pseudo
     * random number generator.
     *
     * @return  A randomly generated {@code UUID}
     */
    public static UUID randomUUID() {
        SecureRandom ng = Holder.numberGenerator;

        byte[] randomBytes = new byte[16];
        ng.nextBytes(randomBytes);
        randomBytes[6]  &= 0x0f;  /* clear version        */
        randomBytes[6]  |= 0x40;  /* set to version 4     */
        randomBytes[8]  &= 0x3f;  /* clear variant        */
        randomBytes[8]  |= 0x80;  /* set to IETF variant  */
        return new UUID(randomBytes);
    }

    /**
     * Static factory to retrieve a type 3 (name based) {@code UUID} based on
     * the specified byte array.
     *
     * @param  name
     *         A byte array to be used to construct a {@code UUID}
     *
     * @return  A {@code UUID} generated from the specified array
     */
    public static UUID nameUUIDFromBytes(byte[] name) {
        MessageDigest md;
        try {
            md = MessageDigest.getInstance("MD5");
        } catch (NoSuchAlgorithmException nsae) {
            throw new InternalError("MD5 not supported", nsae);
        }
        byte[] md5Bytes = md.digest(name);
        md5Bytes[6]  &= 0x0f;  /* clear version        */
        md5Bytes[6]  |= 0x30;  /* set to version 3     */
        md5Bytes[8]  &= 0x3f;  /* clear variant        */
        md5Bytes[8]  |= 0x80;  /* set to IETF variant  */
        return new UUID(md5Bytes);
    }

java 中的 UUID 类位于 java.util 包下

randomUUID 这个方法是类型 4 的随机数生成的,也是我们最常用的

nameUUIDFromBytes 这个则是类型 3 基于名称生成的

我们来通过代码验证一下

UUID uuid1 = UUID.nameUUIDFromBytes(new byte[]{'a', 'b', 'c'});
System.out.println("uuid1 = " + uuid1);
UUID uuid2 = UUID.nameUUIDFromBytes(new byte[]{'a', 'b', 'c'});
System.out.println("uuid2 = " + uuid2);
UUID uuid3 = UUID.randomUUID();
System.out.println("uuid3 = " + uuid3);
UUID uuid4 = UUID.randomUUID();
System.out.println("uuid4 = " + uuid4);
uuid1 = 90015098-3cd2-3fb0-9696-3f7d28e17f72
uuid2 = 90015098-3cd2-3fb0-9696-3f7d28e17f72
uuid3 = 2fd31523-bffb-4a7d-8cb9-a8b9c398822e
uuid4 = af06ce71-6da4-4fdc-bc2b-096c99dbaba5

用 nameUUIDFromBytes 这个方法,如果传入的字符数组相同,那么生成 UUID 也会相同

而用 randomUUID 这个方法则不会生成相同的 UUID

冲突概率

UUID 的标准型式包含 32 个 16 进制数字,以连字号分为五段,形式为 8-4-4-4-12 的 32 个字符。示例:550e8400-e29b-41d4-a716-446655440000

因为是 16 进制,每个位置有 16 种情况,共 32 个字符,理论上能生成不重复的结果总共有 16^32=2^128 种,约等于 3.4 x 10^38

这是一个非常非常大的数字,想想看,一亿也只是 10^8

我们再来计算冲突的概率

假设已经生成了 1 个 UUID,那么下一次生成发生冲突的概率是 1/3.4 x 10^38

假设已经生成了 2 个 UUID,那么下一次生成发生冲突的概率是 2/3.4 x 10^38

...

随着我们已经生成的 UUID 不断变多,下次发生冲突的概率也在不断变大,但是因为总量实在太大,所以尽管概率一直在变大,也可以完全忽略不计

总结

严谨来讲,UUID 可能会发生冲突,因为总量大,发生冲突的可能性极小,可以忽略不计

优点:

  • 本地生成 ID,不需要进行远程调用,没有网络耗时
  • 简单快速,没有性能上限

缺点:

  • 可读性差
  • 长度过长,生成的 UUID 通常是 36 位 (包含 -),不适合作为数据库主键,如果作为主键,二级索引 (非主键索引) 会占用很大的空间。
  • 无法保证趋势递增,在 MySQL 的 InnoDB 引擎下,新插入数据会根据主键来寻找合适位置,会导致频繁的移动、分页增加了很多开销。

本文由mdnice多平台发布

点赞
收藏
评论区
推荐文章
Oracle 分组与拼接字符串同时使用
SELECTT.,ROWNUMIDFROM(SELECTT.EMPLID,T.NAME,T.BU,T.REALDEPART,T.FORMATDATE,SUM(T.S0)S0,MAX(UPDATETIME)CREATETIME,LISTAGG(TOCHAR(
皕杰报表之UUID
​在我们用皕杰报表工具设计填报报表时,如何在新增行里自动增加id呢?能新增整数排序id吗?目前可以在新增行里自动增加id,但只能用uuid函数增加UUID编码,不能新增整数排序id。uuid函数说明:获取一个UUID,可以在填报表中用来创建数据ID语法:uuid()或uuid(sep)参数说明:sep布尔值,生成的uuid中是否包含分隔符'',缺省为
速看!今天我才知道,UUID还分五个版本
通用唯一识别码(英语:UniversallyUniqueIdentifier,缩写:UUID)是用于计算机(https://zh.wikiped
MaxSky MaxSky
4年前
PHP 微信公众号消息加解密
公众号配置根据提示设置即可:【图中信息均为无意义数据,仅供参考。注意服务器地址需可接收GET/POST两种请求】AESKey直接点一下随机生成即可,Token可以生成一个UUID再把UUID进行MD5一次即可。接收关注事件消息示例请求参数校验这一步根据项目情况,可供参考:(Lumen框架)php$valida
Wesley13 Wesley13
4年前
VirtualBox导入已安装好的操作系统的方法
VirtualBox导入已安装好的操作系统的方法1、修改UUID进入VirtualBox安装目录,运行VBoxManage修改UUID,命令运行如下:D:\\VirtualBoxVBoxManage.exe internalcommands setvdiuuid E:\\VirtualX
Stella981 Stella981
4年前
KVM调整cpu和内存
一.修改kvm虚拟机的配置1、virsheditcentos7找到“memory”和“vcpu”标签,将<namecentos7</name<uuid2220a6d1a36a4fbb8523e078b3dfe795</uuid
Easter79 Easter79
4年前
Twitter的分布式自增ID算法snowflake (Java版)
概述分布式系统中,有一些需要使用全局唯一ID的场景,这种时候为了防止ID冲突可以使用36位的UUID,但是UUID有一些缺点,首先他相对比较长,另外UUID一般是无序的。有些时候我们希望能使用一种简单一些的ID,并且希望ID能够按照时间有序生成。而twitter的snowflake解决了这种需求,最初Twitter把存储系统从MySQL迁移
Wesley13 Wesley13
4年前
(绝对有用)iOS获取UUID,并使用keychain存储
UDID被弃用,使用UUID来作为设备的唯一标识。获取到UUID后,如果用NSUserDefaults存储,当程序被卸载后重装时,再获得的UUID和之前就不同了。使用keychain存储可以保证程序卸载重装时,UUID不变。但当刷机或者升级系统后,UUID还是会改变的。但这仍是目前为止最佳的解决办法了,如果有更好的解决办法,欢迎留言。(我整理的解决办法的参
Stella981 Stella981
4年前
Android蓝牙连接汽车OBD设备
//设备连接public class BluetoothConnect implements Runnable {    private static final UUID CONNECT_UUID  UUID.fromString("0000110100001000800000805F9B34FB");
为什么mysql不推荐使用雪花ID作为主键
作者:毛辰飞背景在mysql中设计表的时候,mysql官方推荐不要使用uuid或者不连续不重复的雪花id(long形且唯一),而是推荐连续自增的主键id,官方的推荐是auto_increment,那么为什么不建议采用uuid,使用uuid究
分布式系统的主键生成方案对比 | 京东云技术团队
UUID​UUID(通用唯一识别码)是由32个十六进制数组成的无序字符串,通过一定的算法计算出来。为了保证其唯一性,UUID规范定义了包括网卡MAC地址、时间戳、名字空间(Namespace)、随机或伪随机数、时序等元素,以及从这些元素生成UUID的算法。
哈希珊瑚
哈希珊瑚
Lv1
露从今夜白,月是故乡明。
文章
3
粉丝
0
获赞
0