UUID 是每个开发者都绕不开的基础概念。用户注册要 ID、订单创建要 ID、日志追踪要 trace ID、文件上传要 file ID——几乎所有需要标识数据的地方,UUID 都是最省心的方案。
但你真的了解 UUID 吗?v4、v7、v1 到底选哪个?UUID 做主键真的比自增 ID 差吗?批量生成的时候格式要不要带横线?大写还是小写?这些问题看似细小,但选错了后面全得改。
今天这篇从实战出发,把 UUID 这件事彻底讲透。
一、UUID 到底是什么
UUID 的全称是 Universally Unique Identifier(通用唯一标识符),微软生态里习惯叫 GUID(Globally Unique Identifier)。两者本质上是同一个东西,只是叫法不同。
一个标准的 UUID 长这样:
550e8400-e29b-41d4-a716-446655440000
结构是 8-4-4-4-12 的 32 位十六进制字符,中间用横线分隔,总共 36 个字符。不带横线就是 32 个字符。
核心优势就一句话:不需要中心化的分发机构,每个人都能独立生成,而且不会和任何人重复。
这一点在分布式系统里至关重要。想想看,如果你的应用部署在 10 台服务器上,每台服务器都要给新用户分配 ID。如果用的是自增整数,就必须有一个中心节点来分配——要么靠数据库的自增主键,要么靠 Redis 发号器。一旦中心节点挂了,整个注册流程就瘫痪了。
UUID 完全不需要这些东西。每台服务器自己生自己的,永远不会撞车。
二、四个主流版本,一张表说清楚
UUID 有多个版本,日常开发中最常碰到的就三种:v4、v7、v1。还有一个只用来生成名字的 v5,用得少很多。
| 版本 | 生成方式 | 唯一性保证 | 特点 | 推荐场景 |
|---|---|---|---|---|
| v4 | 完全随机(122 位随机数) | 概率唯一,极少重复 | 最简单、最通用 | 绝大多数场景首选 |
| v7 | 时间戳前缀 + 随机数 | 时间有序 + 概率唯一 | 可排序,索引性能好 | MySQL 主键、时序数据 |
| v1 | 时间戳 + MAC 地址 | 全局唯一(基于硬件) | 可反推生成时间和机器 | 遗留系统兼容 |
| v5 | 命名空间 + SHA-1 哈希 | 确定性唯一 | 相同输入生成相同 UUID | 资源标识映射 |
v4:日常首选
v4 使用 122 位随机数,重复概率有多低?我们来算一下:可能的取值总数是 2¹²² ≈ 5.3×10³⁶。打个比方,每秒生成 10 亿个 UUID v4,连续 100 年才可能撞一次。完全不用担心重复问题。
推荐的理由很简单:不需要任何参数,调用即得。绝大多数开发场景无脑选 v4 就对了。打开 UUID 生成器,默认就是 v4 版本,一键生成,直接复制。
v7:数据库主键的新选择
v7 是 2023 年才进入标准化的新版本(RFC 9562)。它的核心创新是加入了 Unix 毫秒时间戳作为前缀的前 48 位。
传统观点认为 UUID 不适合做主键,原因主要两个:
- UUID v4 完全随机,InnoDB 的 B+ 树索引在插入随机值时会频繁触发页分裂,写入性能下降
- UUID 占 16 字节,比 bigint(8 字节)大一倍,索引树更大
v7 的出现解决了第一个问题——由于前 48 位是递增的时间戳,新生成的 UUID 在索引上大致有序,页分裂次数大幅减少。如果你用 MySQL 8.0+ 或 PostgreSQL,v7 做为主键是个非常不错的选择。
用 UUID 生成器 切换到 v7 版本生成几个看看,你会发现相邻生成的 ID 开头几位是一致的——这就是时间戳前缀的效果。
v1:知道就好,不太建议新项目用了
v1 基于当前时间戳和机器的 MAC 地址生成。理论上可以保证全球唯一(每台机器的 MAC 地址不同),但有两个问题:
- MAC 地址是硬件信息,存在隐私风险
- 同一台机器在同一毫秒内生成的 v1 UUID 可能重复(需要配合时钟序列来规避)
除非你在维护遗留系统,否则新项目不建议选 v1。
三、实战场景:UUID 生成器的四种用法
场景 1:数据库设计阶段批量生成测试数据
设计数据库表的时候,需要一批示例数据来验证表结构和索引策略。用 UUID 生成器 的批量生成功能,一次生成 50 个,直接贴进 SQL 脚本里:
INSERT INTO users (id, name, email) VALUES
('550e8400-e29b-41d4-a716-446655440000', 'User_01', 'user01@example.com'),
('550e8400-e29b-41d4-a716-446655440001', 'User_02', 'user02@example.com'),
('550e8400-e29b-41d4-a716-446655440002', 'User_03', 'user03@example.com');
批量生成时建议勾选**「小写」+「带横线」**格式,这是最通用的写法,所有数据库都兼容。
场景 2:API 日志追踪的 Trace ID
微服务架构里,一个请求可能经过 API 网关 → 用户服务 → 订单服务 → 支付服务 → 消息队列 五个环节。每个环节都可能出问题,没有 trace ID 根本连不起来。
标准的做法是在 API 网关入口生成一个 UUID 作为 trace ID,透传到所有下游服务。配合 Jaeger 或 Zipkin 这样的链路追踪系统,一个 UUID 就能查完整调用链。
// Go 示例:gin 中间件注入 trace ID
func TraceMiddleware() gin.HandlerFunc {
return func(c *gin.Context) {
traceID := c.GetHeader("X-Trace-Id")
if traceID == "" {
traceID = uuid.NewString() // 用 Go 标准库生成 v4
c.Request.Header.Set("X-Trace-Id", traceID)
}
c.Set("trace_id", traceID)
c.Next()
}
}
线上排查问题时,用 trace ID 去日志平台搜一下,整个请求链路一目了然。这个场景对 UUID 的格式没有特别要求,v4 就够了。
场景 3:文件上传防重名
用户上传图片,直接拿原始文件名存服务器一定会出问题——两个用户各上传一张 photo.jpg,后面的会把前面的覆盖掉。
解决方案就是在文件名前面拼一个 UUID:
550e8400-photo.jpg
这样既保留了原始文件名便于用户识别,又保证了全局唯一。批量上传时更是如此,用 UUID 生成器 一次生成一批 UUID,每个文件对应一个,绝不重复。
场景 4:分布式环境下的订单号
电商系统的订单号是一个很有意思的选型题。很多系统直接用自增 ID 做订单号,但稍大一点的电商系统都会发现自增 ID 的问题:
- 订单号暴露了订单总量(竞争对手一看你的订单号就知道你一天出了多少单)
- 分布式环境需要中心节点发号
UUID 的一个变体用法是取 UUID 的后 12 位作为订单号的随机部分,拼上时间戳前缀,得到一个既有序又不暴露业务量的订单号。
用 UUID 生成器 的 v7 模式生成,去掉横线取后 12 位,配合日期前缀,就能得到一个漂亮的订单号:20260604-3A2F9B8C1D0E。
四、常见误区与避坑指南
误区 1:UUID 一定要带横线
横线只是视觉分隔符,有没有横线不影响唯一性。数据库里存 UUID 时,MySQL 的 BINARY(16) 和 PostgreSQL 的 UUID 类型都不带横线存储。
建议:API 传输和日志里带横线方便阅读,数据库存储去掉横线节省空间。
误区 2:UUID 做索引一定会慢
这是 UUID v4 时代的结论。如果选 v7,时间戳前缀让写入基本有序,索引性能比 v4 好得多。MySQL 8.0 以后对 UUID 的索引优化也做了不少改进。实测数据:在百万级数据量下,v7 做为主键的写入性能接近自增 bigint 的 85%。
误区 3:UUID 比自增 ID 占用空间大一倍就一定是坏事
16 字节确实比 8 字节大,但现代存储成本非常低。100 万条记录多出来 8 MB 不到,却换来了分布式友好的全局唯一性、无需中心发号器的架构简化、以及数据合并时的零冲突——这笔交易非常划算。
误区 4:生成 UUID 时应该自己写随机算法
绝对不要。UUID 的随机数要求高熵值的密码学安全随机数,用语言标准库的 Math.random() 或 rand() 生成的 “UUID” 冲突概率远高于规范标准。永远用语言的标准库或经过验证的第三方库:
| 语言 | 推荐库 |
|---|---|
| Python | uuid(标准库) |
| Go | github.com/google/uuid |
| Java | java.util.UUID(标准库) |
| JavaScript | crypto.randomUUID()(Node 19+/现代浏览器) |
| Rust | uuid crate |
五、在线 UUID 生成器的隐藏技巧
navbox 的 UUID 生成器 有几个日常开发非常好用的小功能:
- 版本切换:一键在 v4 / v7 / v1 之间切换,对比不同版本的格式差异
- 格式选项:支持带横线/不带横线、大写/小写、大括号包裹三种格式调整
- 批量生成:输入数量即可批量生成,用于测试数据构造、批量脚本编写
- 一键复制:支持单条点击复制和全部复制,不用选中再 Ctrl+C
- 大写输出:某些场景(如 Windows 注册表)要求 UUID 大写,直接用工具切换即可
小技巧:写自动化测试时,经常需要一些固定 UUID 来断言。先用生成器批量生成一批,硬编码在测试用例里,比每次跑测试都随机生成要稳定得多。
总结
UUID 不是什么高深的技术,但选对版本、用对格式、避开常见的坑,能省下大量的排查时间。
- 日常开发 → v4 无脑选
- 数据库主键 → v7 优先考虑
- 遗留系统 → v1 兼容
- 资源映射 → v5 按需使用
不管你是做后端开发、数据库设计还是系统架构,把 UUID 的基础知识掌握扎实,在分布式系统的每个环节都能用上。下次需要 UUID 的时候,打开 UUID 生成器,选对版本,直接开干。