UUID v4 与 v7:有什么变化以及为何重要

发布: (2026年2月5日 GMT+8 22:12)
7 min read
原文: Dev.to

Source: Dev.to

请提供您希望翻译的具体文本内容,我将按照要求将其翻译成简体中文并保留原始的格式、Markdown 语法以及技术术语。谢谢!

📌 UUID v4 – 纯随机

f47ac10b-58cc-4372-a567-0d8b62691e10
         ^^^^
         version 4

工作原理

  1. 生成 128 位随机数。
  2. 设置版本号(4 位 → 0100)。
  3. 设置变体(2 位 → 10)。
  4. 8‑4‑4‑4‑12 的十六进制字符串格式化。

优点

  • 极其简单,易于生成。
  • 统计上唯一(2¹²² 种可能的取值)。
  • 各系统之间无需协同。
  • 不泄露信息(没有时间戳、没有 MAC 地址)。

缺点

  • 完全随机 → 对数据库索引极为不友好。
  • 没有自然顺序。
  • 无法提取任何有用的元数据。
  • 会导致数据库中 B‑树索引碎片化

UUID v4 为我们提供了很好的服务,但“足够好”并不等同于“最优”。

🚀 UUID v7 – 时间有序、数据库友好

019526de-a3c0-7cc0-b2e8-4a1b3c5d7e9f
^^^^^^^^ ^^^^
timestamp  version 7

工作原理

  1. 获取当前 Unix 时间戳(毫秒,48 位)。
  2. 设置版本号(4 位 → 0111)。
  3. 设置变体(2 位 → 10)。
  4. 用随机数据填充剩余的 62 位。
  5. 按标准 UUID 字符串格式化。

结构

位数含义
48时间戳(毫秒)
4版本(7
12随机 a
2变体(10
62随机 b

优点

  • 天然时间有序 – 字典序排序即为时间顺序。
  • 数据库友好:顺序插入 → 最小化 B‑树页分裂。
  • 可提取时间戳 – 能读取 UUID 的创建时间。
  • 仍然全局唯一(62 位随机 + 毫秒时间戳)。
  • 直接替换:同样的 128 位大小,同样的字符串格式。

权衡

  • 泄露创建时间(前 48 位是时间戳)。
  • 随机性略低于 v4(62 位 vs 122 位随机)。
  • 较新 – 并非所有库都已支持。

📈 为什么主键的顺序很重要

当你在 B‑tree 索引的表(大多数表的默认索引方式)中使用 UUID v4 作为主键时,每次 INSERT 都会在索引的 随机位置 插入,导致:

  • 页面分裂 – 不断进行 B‑tree 重组。
  • 缓存未命中 – 没有局部性。
  • 写放大 – 每次插入需要更多 I/O。
  • 索引膨胀 – 页面碎片化,空间浪费。

在大规模环境下,这会严重影响性能。

UUID v7 解决了这些问题

由于 UUID v7 值是 时间有序 的,新的插入始终位于 B‑tree 的 末尾

  • 顺序写入 – 追加而非随机插入。
  • 更好的缓存利用 – 最近的数据位于同一位置。
  • 更少的页面分裂 – 树自然增长。
  • 更小的索引 – 碎片更少。

基准测试始终显示,在 PostgreSQL 和 MySQL 上,使用 UUID v7 相比 UUID v4 的 INSERT 性能提升 2‑10 倍,具体取决于表的大小和工作负载。

✅ 何时使用哪个版本

场景推荐
数据库主键v7
API 请求追踪v7
会话令牌v4
事件溯源v7
密码重置令牌v4
分布式日志条目v7
缓存键(不需要排序)v4

决策矩阵(快速浏览)

需求使用 v4使用 v7
零信息泄露(时间戳敏感)
为机密/令牌提供最大熵
不需要排序(内存查找、哈希表)
系统仅支持 v4
ID 用作数据库主键(主要使用场景)
需要自然的时间顺序
需要从 ID 中提取创建时间
分布式系统需要时间顺序的事件
关注大规模时的数据库性能

🛠️ 如何生成 UUID v7

Node.js (v20+)

import { randomUUID } from 'crypto';

// v4 only (as of Node 22)
const idV4 = randomUUID();

要生成 v7,请使用库:

import { v7 as uuidv7 } from 'uuid';
const idV7 = uuidv7();

Python

import uuid

# v4
uuid.uuid4()
# v7 (Python 3.14+ or uuid7 package)
from uuid_extensions import uuid7
uuid7()

PostgreSQL

-- v4 (built‑in)
SELECT gen_random_uuid();

-- v7 (PostgreSQL 17+ or pg_uuidv7 extension)
SELECT uuid_generate_v7();

在线

createuuid.com – 在浏览器中即时生成 v1、v4 和 v7,并支持批量生成。

🕵️ 从 UUID v7 中提取时间戳

function extractTimestamp(uuidV7) {
  const hex = uuidV7.replace(/-/g, '');
  const timestampHex = hex.substring(0, 12); // first 48 bits = 12 hex chars
  const timestampMs = parseInt(timestampHex, 16);
  return new Date(timestampMs);
}

// Example
extractTimestamp('019526de-a3c0-7cc0-b2e8-4a1b3c5d7e9f');
// → 2025‑02‑05T…

这在调试、审计以及在没有额外列或元数据的情况下理解数据流时非常有用。

🤔 我应该将现有的 v4 列迁移到 v7 吗?

简短回答: 可能不需要。

现有的 v4 UUID 正常工作。将生产数据库中的主键迁移的成本几乎从不值得。相反:

  1. 在新表和新服务中使用 v7。
  2. 在已有的地方保留 v4。
  3. 让系统的自然生命周期来完成过渡。

例外情况: 如果您在大规模下因 UUID v4 索引碎片导致真实的性能问题——并且已确认 UUID 是瓶颈——则可以考虑有计划的迁移。

📊 快速比较

功能UUID v4UUID v7
引入时间RFC 4122 (2005)RFC 9562 (2024)
随机位数12262
是否时间有序?
大规模数据库性能优秀
时间戳可读性?
最适用场景令牌、密钥主键、事件

🎯 结论

  • UUID v7 不是 v4 的替代品——它是针对另一种(且非常常见)工作场景的合适工具。
  • 如果你正在启动新项目,并且你的 UUID 将用作 数据库主键v7 是更好的默认选择
  • 对于 令牌、密钥,或任何不需要排序的情况,请坚持使用 v4

现在就需要 UUID 吗?

试试 createuuid.com ——免费、即时、无需注册。

本文是 Developer Tools Deep Dives 系列的一部分,我们将在其中解释你每天使用的工具背后的“原因”。

Back to Blog

相关文章

阅读更多 »

如何免费学习 Python?

嗨!我对技术世界是新人,想学习 Python 开启我的编程之旅。我已经学过一点 HTML,但我意识到我并不喜欢网页开发……