UUID 还是雪花 ID?分布式唯一 ID 方案怎么选?

一、为什么分布式 ID 是个难题

在单体数据库时代,自增主键(AUTO_INCREMENT)就够用了。但系统一旦拆成多节点、多库分表,自增主键立刻撞墙:两个节点会生成相同的 1、2、3,合并时冲突。

于是业界需要一种"不依赖中心、各节点各自生成、全局不重复"的 ID。主流方案分成两派:

  • UUID 派:拿一段标准化的随机/时间戳串,128 位,跨语言跨库即插即用。
  • 雪花派(Snowflake):把"时间戳 + 机器号 + 序列号"拼进一个 64 位整数,趋势递增、带时间语义。

下面先分别拆开看,再正面 PK。

二、两大流派总览

维度 UUID(以 v4 / v7 为代表) 雪花算法 Snowflake
位宽 128 位 64 位(long 型)
形态 带连字符的十六进制串(如 550e8400-e29b-41d4-a716-446655440000 一个长整型数字(如 1765383830000000000
核心思想 靠足够大的随机空间保证不重复 靠"时间 + 节点 + 序号"组合保证不重复且有序
是否有序 v4 完全无序;v7 时间有序 趋势递增(按时间)
是否依赖时钟 是(强依赖本机时钟)
是否依赖网络/中心 否(但需分配机器 ID)
标准 RFC 4122 / RFC 9562 Twitter 开源算法,无官方标准

三、UUID:128 位的标准标识符

UUID(Universally Unique Identifier)由 RFC 4122 定义,共 128 位,通常以 8-4-4-4-12 的十六进制呈现为 32 字符(加连字符 36 字符)。常见版本:

  • v1 :时间戳 + MAC 地址。会泄露网卡 MAC,隐私风险大,基本不用
  • v4 :122 位密码学强随机数。最常见的版本,靠概率保证唯一(碰撞概率约 1/2^61,可忽略)。
  • v6 / v7 / v8 :RFC 9562(2024-04 发布)新增。其中 v7 把 48 位毫秒时间戳放在最前面,其余为随机位------天然时间有序,专门用来当数据库主键。
  • v8:自定义字段,留给业务扩展。

示例(v4,浏览器 crypto.randomUUID() 生成):

text 复制代码
550e8400-e29b-41d4-a716-446655440000

优点 :无需协调、跨语言跨库原生支持、断网也能生成。 缺点:v4 完全无序,当主键时 B-tree 索引会频繁分裂、碎片化(这是 MySQL InnoDB / SQL Server 上的经典坑)。

四、雪花算法:64 位时间戳拼装

Snowflake 是 Twitter 2010 年开源的分布式 ID 算法。它把一个 64 位 long 拆成三段(最高位符号位恒为 0):

text 复制代码
 0 | 0000000000 0000000000 0000000000 0000000000 0000000000 0 | 0000000000 | 000000000000
   |←────────────── 41 位时间戳(毫秒)──────────────→|← 5+5 机器ID →|← 12 位序列号 →|
                                                                  (机房+机器,共 1024 节点)
  • 41 位时间戳 (毫秒):从自定义纪元起算,可用约 69 年(到 2082 年左右)。
  • 10 位机器 ID :通常 5 位数据中心 + 5 位机器,最多部署 1024 个节点
  • 12 位序列号 :单节点每毫秒最多生成 4096 个 ID ,理论单机 QPS ≈ 409 万

核心生成逻辑(简化 Java):

java 复制代码
long id = ((timestamp - twepoch) << 22)   // 时间戳左移
        | (workerId << 12)                // 机器 ID
        | sequence;                       // 序列号

优点 :本地生成无网络依赖、性能极高、趋势递增、自带时间语义(ID 里能读出生成时刻)。 缺点 :强依赖本机时钟------时钟回拨(NTP 校时、手动改时间)会导致 ID 重复 ;机器 ID 需靠注册中心(ZK/Nacos)分配,配错会冲突;64 位长整型在前端 JS(number 只有 53 位精度)里会丢失精度,通常要转成字符串传输。

业界改进:百度 UidGenerator(环形数组缓存序列号、弱化时间依赖)、美团 Leaf-snowflake(ZK 管机器 ID + 时钟回拨检测)、滴滴 Tinyid(号段模式)。

五、UUID vs 雪花:正面对比

对比维度 UUID(v4/v7) 雪花算法 Snowflake
位宽 / 存储 128 位,二进制 16 字节 64 位,8 字节(更省)
文本长度 36 字符(v4) 19~20 位十进制串
有序性 v4 无序;v7 有序 趋势递增
全局唯一保障 概率保证(随机空间极大) 时间+节点+序号组合,确定性唯一
是否依赖时钟 是,时钟回拨会翻车
机器 ID 管理 不需要 需要(易冲突,需中心分配)
前端友好度 字符串直传,OK 64 位整数需转字符串(防 JS 精度丢失)
生成性能 极高(本地随机) 极高(本地拼接,≈409 万 QPS/节点)
时间可推导 v7 可、v4 不可 可(ID 里含时间戳)
跨语言/库生态 RFC 标准,几乎全原生支持 各语言有实现,但无统一标准
安全/可预测 v4 不可预测;v7 泄露创建时间 连续可预测,会暴露订单量/用户量

六、怎么选:决策表

  • 选 UUID(尤其 v7)当 :单库或多语言微服务、不想运维机器 ID、希望直接复用数据库原生 uuid 类型、接受 16 字节存储。新项目首选 UUID v7(有序 + 标准)。
  • 选雪花(或 Leaf/Tinyid)当 :超高并发、需要极致紧凑的 64 位整数主键、内部系统不暴露 ID、且有能力处理好时钟回拨与机器 ID 分配(有运维/基建支撑)。
  • 都别用 v4 当大表主键:无序写入的索引碎片问题在千万行后会很明显。

一句话:要省心、跨语言、标准 → UUID v7;要极致紧凑、可控、有基建兜底 → 雪花

七、折中方案:UUID v7 与 ULID

如果你既想要"时间有序"(解决索引碎片),又想要"UUID 那套 128 位、标准、跨库即用"的好处,有两个现成选择:

  • UUID v7 :RFC 9562 标准,前 48 位放毫秒时间戳,后 74 位随机。格式仍是 UUID,能直接放进既有 uuid 列。PostgreSQL 18 已原生 uuidv7(),Python 3.13 有 uuid.uuid7()
  • ULID :128 位,但用 26 字符的 Crockford Base32 表示(无连字符、大小写不敏感、去掉了易混的 I/L/O/U)。前 10 字符是 48 位毫秒时间戳、后 16 字符是 80 位随机数,字典序即时间序,比 UUID 短 10 个字符,URL/日志更友好。

简单说:UUID v7 = 标准里的有序方案;ULID = 更短更友好的有序方案。两者解决的是同一个问题,新项目优先 v7(生态最全),要短字符串就上 ULID。

八、动手试试:在线工具

不用装库,浏览器里就能生成对比:

在你的浏览器本地完成,关掉网线照样能生成,令牌不会发到服务器。

九、常见问题 FAQ

Q1:雪花 ID 在 JavaScript 里为什么精度丢了? A:JS 的 number 只有 53 位有效数字,而雪花是 64 位整数。后端务必把 ID 序列化成字符串再传给前端,否则末尾几位会被四舍五入。

Q2:UUID v4 当主键真的不能用吗? A:小表无所谓;大表(千万行+)会因无序插入导致 B-tree 碎片、写入劣化。要么换 v7/ULID,要么用 BINARY(16) 存储并在应用层排序。

Q3:雪花时钟回拨怎么处理? A:回拨很小(如 <5ms)就等待追平;回拨较大则拒绝生成或切换备用时钟;Leaf-snowflake 还会在启动时用 ZK 校验上次时间,回拨直接启动失败。

Q4:UUID v7 和 ULID 该选哪个? A:同解决有序 ID 问题。技术栈已有原生 uuid 类型/类库 → 选 v7;要更短、URL 友好的文本 → 选 ULID。两者都是 128 位、二进制存储成本相同。

Q5:雪花 ID 会暴露业务数据吗? A:会。连续 ID 可被竞品推算出订单量、用户增速;且 ID 内嵌时间戳。对外暴露的场景建议用随机性更强的 UUID,或对 ID 做不可逆变换。

十、总结

UUID 和雪花算法不是谁替代谁,而是适用面不同

  • UUID(v7) :标准、跨语言、省心,适合大多数现代后端,新项目默认推荐
  • 雪花(Snowflake/Leaf):紧凑 64 位、可控、需基建兜底,适合超高并发且对内不暴露 ID 的场景。
  • ULID:在"有序 + 短字符串"上比 UUID 更友好,是 v7 之外的轻量备选。

选型记住一条主线:要标准省心选 UUID v7,要极致紧凑且有运维能力选雪花,要短而有序选 ULID

相关推荐
吃饱了得干活1 小时前
Java设计模式实战:一个支付模块的重构之旅,层层递进理解设计模式精髓
后端·设计模式·架构
江湖十年1 小时前
Go 还是 Golang?可能你一直都搞错了!
后端·面试·go
计算机毕设定制辅导-无忧学长1 小时前
《基于SpringBoot的庭院玫瑰栽培养护知识交互式科普平台设计与实现》
java·spring boot·后端·毕业设计·个性化推荐·协同过滤算法·庭院玫瑰栽培养护知识科普平台
骇客野人1 小时前
SpringBoot数十Jar包批量生产部署落地实施方案(Shell+Systemd完整版)
spring boot·后端·jar
Json____2 小时前
从零构建家政服务平台:一套全栈架构如何打通管理端与移动端-java-springboot
java·spring boot·后端·架构·毕设·wwwoop.com
leeyi2 小时前
命令行里的平台:df_cli 都会什么(第108篇)
后端·aigc·agent
云雀衔光3 小时前
Java Spring AI MCP:在 Spring 里把 AI 接上你的业务系统
java·开发语言·人工智能·后端·测试工具·spring
苏渡苇3 小时前
Spring Insight 里如何对 Span 进行清洗
java·spring boot·后端·spring·系统监控
Moment3 小时前
为什么市面上的 coding agent 大多数都基于Nodejs?
前端·后端·面试