UUID v1 v4 v7 怎么选?数据库主键用哪个?

UUID 数据库 后端

UUID v1 v4 v7 怎么选?数据库主键用哪个?

每个项目都要选 UUID 版本。v1 太老、v4 太随机、v7 太新。这篇给出三种版本的对比和场景化推荐。

想直接生成?打开 RuMystic UUID 生成器 — 支持 v1 v4 v7,批量生成,本地运行。


三个版本的核心区别

版本生成方式可排序随机性适用场景
v1时间戳 + MAC 地址是(按时间)老系统兼容
v4全随机通用默认
v7毫秒时间戳 + 随机数据库主键

UUID v1:时间 + MAC

结构:60 位时间戳(100 纳秒精度,从 1582-10-15 起算)+ 48 位 MAC 地址 + 14 位时钟序列。

优点

  • 可排序(按生成时间)
  • 不需要协调,每台机器独立生成
缺点
  • 泄漏生成机器的 MAC 地址(隐私问题)
  • 不同机器时钟不同步可能重复
  • 时间戳起算点是 1582 年(格里高利历改革),不直观
  • RFC 9562(2024)已把它标记为「不推荐新系统使用」
用在哪:旧系统兼容。新系统别选。


UUID v4:全随机

结构:122 位随机数 + 4 位版本号 + 2 位变体号。

优点

  • 实现简单,crypto.randomUUID() 一行搞定
  • 碰撞概率极低(2.71 × 10¹⁸ 个才有 50% 碰撞)
  • 不泄漏任何信息
缺点
  • 不可排序 — 生成时间相近的两个 v4 完全无序
  • 数据库 B-tree 索引碎片化严重(随机插入)
  • 索引膨胀,写入性能随表增长下降
用在哪
  • 临时令牌(密码重置、分享链接)
  • 不存数据库的 ID(前端临时 ID)
  • 不需要按时间排序的场景

UUID v7:时间排序 + 随机

结构(RFC 9562,2024 年标准):

  • 48 位 Unix 毫秒时间戳(前面)
  • 12 位随机数
  • 62 位随机数 + 变体
优点
  • 可排序 — 字典序 = 时间序,B-tree 索引顺序插入
  • 数据库索引不碎片化,写入快 1.5-3 倍
  • 日志关联、事件溯源、对象存储 key 天然有序
  • 不泄漏 MAC 地址
缺点
  • 生成时间可推断(泄漏创建时间,类似 created_at 字段)
  • 同一毫秒内大量并发写入可能集中在 B-tree 最后一页(罕见,需要 10 万+ TPS 才显现)
  • 较新,部分老系统/库不支持
用在哪
  • 数据库主键(最推荐)
  • 事件溯源的 event ID
  • 日志的 trace ID
  • 对象存储的 key(按时间排序扫描快)

数据库性能对比

在 Postgres 插入 1 亿行,主键分别为 v4 和 v7:

指标v4v7
插入耗时100%50-70%
索引大小100%60-80%
缓存命中率较低较高
范围扫描(按时间)慢(需扫全表)快(物理连续)

原因:v4 随机插入触发 B-tree 页分裂,v7 顺序追加到最后一页,几乎不分裂。


安全性考虑

v1 的隐私问题

v1 UUID 包含 MAC 地址。早期 WordPress 用 v1 UUID 做评论 ID,结果泄漏了服务器 MAC 地址。现在新系统都不该用 v1。

v4 的不可预测性

v4 完全随机,适合做 capability token(URL 里的不可猜测 ID)。

v7 的时间泄漏

v7 暴露生成时间。如果你的 ID 用在 URL 里,且「这个对象什么时候创建」是敏感信息(比如用户注册时间),不要直接用 v7。可以:

  • 用 v7 做内部主键,对外用 v4 做公开 ID
  • 或者 hash v7 后再暴露

各语言支持(2026 年)

语言v1v4v7
JavaScriptuuidcrypto.randomUUID()uuid v11+
Pythonuuid.uuid1()uuid.uuid4()uuid.uuid7() (3.13+) 或 uuid_utils
Gogoogle/uuidgoogle/uuidgoogle/uuid v1.6+
JavaUUIDUUID.randomUUID()uuid-creator
.NETGuidGuid.NewGuid()Guid.CreateVersion7() (.NET 9+)
Postgresuuid-osspgen_random_uuid()pgcrypto 1.5+ 或应用层生成

选择决策树

你的 UUID 会作为数据库主键吗?
├─ 是 → 用 v7(顺序插入,索引不碎片)
└─ 否 → 你的 UUID 会出现在 URL 里且需要不可猜测吗?
        ├─ 是 → 用 v4(完全随机,适合做 token)
        └─ 否 → 你需要按时间排序吗?
                ├─ 是 → 用 v7
                └─ 否 → 用 v4(最通用,所有库都支持)

实战建议

新项目

数据库主键一律 v7。前端展示用 v7 没问题(除非创建时间敏感)。临时 token 用 v4。

老项目迁移

不要把已有 v4 主键改成 v7 — 索引会乱。新表用 v7,老表保持 v4。

分布式系统

v7 不需要协调,每台机器独立生成。同一毫秒内的碰撞概率(2.4 × 10¹¹ 才有 50% 碰撞)可以忽略。

事件溯源 / 日志

v7 是最佳选择。trace ID 用 v7,按时间排序天然成立,不用额外加 timestamp 字段做复合索引。


生成测试

想看三种 UUID 长什么样?打开 RuMystic UUID 生成器,选版本、设数量、一键生成。

v1: 6b1d1a20-0822-11f1-8a3c-0242ac130003
v4: 4f7c8a92-3b1e-4d5f-9a2c-8e1f0b3a4c5d
v7: 0196398a-7c4e-7d2b-a3f1-4c8e1b2a3d5e

注意 v7 开头 0196398a 是 Unix 毫秒时间戳的十六进制,2026 年的开头大概是 019 开头。


总结

  • v1 — 别用,老古董
  • v4 — 通用默认,做 token / 临时 ID
  • v7 — 数据库主键、日志、事件溯源首选
90% 的新项目,数据库主键用 v7,对外 token 用 v4,就这两条规则。