什么是 UUID,能保证唯一性吗?


一则或许对你有用的小广告

欢迎加入小哈的星球,你将获得:专属的实战项目(4个项目都能学) / 1v1 提问 / 简历修改 / Java 学习路线 / 社群讨论 / 学习打卡 / 每月赠书

  • 《Spring AI 项目实战(问答机器人、RAG 智能客服、联网搜索)》已完结,基于 Spring AI + Spring Boot 3.x + JDK 21...查看介绍

  • 《从零手撸:仿小红书(微服务架构)》 已完结,基于 Spring Cloud Alibaba + Spring Boot 3.x + JDK 17...查看介绍;演示链接:http://116.62.199.48:7070/

  • 《从零手撸:前后端分离博客项目(全栈开发)》 2 期已完结,演示链接:http://116.62.199.48/

  • 新开坑项目:《从零手撸:秒杀系统高并发优化实战》 正在更新中...,查看介绍

截止目前,星球内专栏累计输出 150w+ 字,讲解图 5110+ 张,还在持续爆肝中.. 后续还会上新更多项目,已有 4700+ 小伙伴加入学习,欢迎点击围观

面试考察点

  1. 概念理解:面试官不仅仅是想知道 UUID 是 "一串随机字符",更是想考察你是否理解 UUID 的结构组成生成算法

  2. 唯一性认知:UUID 的 "唯一性" 是理论上的还是绝对的?这涉及概率论和分布式系统的核心概念。

  3. 方案选型:在实际项目中,UUID 是否是最佳选择?考察你是否了解其他分布式 ID 生成方案(雪花算法、数据库自增等)。

核心答案

UUID(Universally Unique Identifier)是通用唯一识别码,一个 128 位的标识符,通常表示为 36 个字符的字符串(32 个十六进制数 + 4 个连字符)。理论上可以保证唯一性,但不是 100% 绝对不重复。

维度 说明
长度 128 位(16 字节)
字符串形式 xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx(36 字符)
示例 550e8400-e29b-41d4-a716-446655440000
唯一性 理论唯一,实际有极小概率冲突(约 2^122 分之一)
Java 生成 UUID.randomUUID()

一句话回答:UUID 在实际应用中可以认为是唯一的,但从数学角度不是绝对不重复,只是冲突概率低到可以忽略不计。

深度解析

一、UUID 的结构组成

上图展示了 UUID 的完整结构。关键要点:

  • 128 位总长度:可以生成 2^128 ≈ 3.4 × 10^38 个不同的 UUID
  • 版本号字段:标识 UUID 的生成方式(版本 1-5)
  • 变体字段:标识 UUID 的变体类型(RFC 4122、Microsoft 等)

二、UUID 的版本

版本 生成方式 唯一性来源 特点
v1 基于时间戳 + MAC 地址 时间 + 网卡地址 可追踪,但有隐私问题
v2 DCE 安全版本 时间 + 用户 ID 很少使用
v3 基于命名空间 + MD5 哈希算法 相同输入产生相同 UUID
v4 随机生成(最常用) 伪随机数 简单高效,Java 默认
v5 基于命名空间 + SHA-1 哈希算法 比 v3 更安全

Java 的 UUID.randomUUID() 生成的是 v4 版本,基于伪随机数生成。

三、UUID 真的能保证唯一性吗?

上图分析了 UUID 的唯一性。关键结论:

  • 理论唯一:2^122 的空间大到人类无法想象,正常使用几乎不可能冲突
  • 实际风险:伪随机数生成器质量、实现缺陷可能导致重复
  • 正确认知:UUID 适合做标识符,不适合做安全凭证

四、Java 中使用 UUID

import java.util.UUID;

public class UUIDDemo {
    public static void main(String[] args) {
        // 生成 UUID v4(随机)
        UUID uuid = UUID.randomUUID();
        System.out.println(uuid);
        // 输出: 550e8400-e29b-41d4-a716-446655440000

        // 获取各部分
        System.out.println("版本: " + uuid.version());      // 输出: 4
        System.out.println("变体: " + uuid.variant());      // 输出: 2
        System.out.println("高位: " + uuid.getMostSignificantBits());
        System.out.println("低位: " + uuid.getLeastSignificantBits());

        // 从字符串创建 UUID
        UUID parsed = UUID.fromString("550e8400-e29b-41d4-a716-446655440000");

        // 去掉连字符(32 字符)
        String uuidNoDash = uuid.toString().replace("-", "");
        System.out.println(uuidNoDash);
        // 输出: 550e8400e29b41d4a716446655440000
    }
}

五、分布式 ID 方案对比

方案 长度 有序性 依赖 适用场景
UUID 128 位 ❌ 无序 非主键场景、临时标识
雪花算法 64 位 ✅ 趋势有序 时钟同步 分布式主键(推荐)
数据库自增 32/64 位 ✅ 严格有序 数据库 单机场景
Redis 原子递增 64 位 ✅ 严格有序 Redis 高并发场景
号段模式 64 位 ✅ 趋势有序 数据库 高性能场景

UUID 作为数据库主键的问题

  • 索引性能差:UUID 无序,导致 B+ 树频繁页分裂
  • 存储空间大:32 字节 vs 雪花算法的 8 字节
  • 可读性差:不如自增 ID 直观

面试高频追问

  1. UUID v1 和 v4 有什么区别?

    版本 v1 v4
    生成方式 时间戳 + MAC 地址 伪随机数
    可追踪性 ✅ 可追踪到机器和时间 ❌ 不可追踪
    隐私问题 ⚠️ 暴露 MAC 地址 ✅ 无隐私问题
    Java 支持 需第三方库 UUID.randomUUID()
  2. 为什么不推荐用 UUID 做数据库主键?

    • UUID 无序,插入会导致 B+ 树页分裂,影响性能
    • UUID 较长(36 字符),占用更多存储空间
    • 推荐使用雪花算法自增 ID 作为主键
  3. 如何解决分布式系统的 ID 唯一性问题?

    • 雪花算法(Snowflake):Twitter 开源,64 位趋势递增
    • Leaf:美团开源,号段模式 + 雪花算法
    • UidGenerator:百度开源,基于雪花算法
    • TinyID:滴滴开源,号段模式

常见面试变体

  • 变体一:"分布式 ID 生成方案有哪些?各有什么优缺点?"
  • 变体二:"雪花算法的实现原理是什么?时钟回拨怎么处理?"
  • 变体三:"UUID 和自增 ID 作为主键,哪个性能更好?为什么?"

记忆口诀

UUID 128 位,v4 随机最常用; 理论唯一非绝对,做主键性能差; 分布式 ID 选雪花,趋势有序效率高。

总结

UUID 是 128 位的通用唯一标识符,Java 默认生成 v4 版本(随机)。虽然理论上存在极小概率冲突,但实际应用中可以认为是唯一的。UUID 不适合作为数据库主键(无序、占用空间大),分布式主键推荐使用雪花算法或号段模式。