分布式 ID 生成策略全景图:UUID、号段、Snowflake、Leaf、TinyID,如何选型?

67 阅读6分钟

前言:

最近在写项目时,面临选择id生成方式的难题时,我发现我并不能清晰的根据使用场景选择一个合适的id生成方式,这篇文章来梳理一下不同id生成策略。

一.简介

1.1、什么是分布式id

分布式ID是指在分布式系统中,由多个节点协同或独立生成的、全局唯一的标识符在分布式系统、数据库设计和业务开发中,ID生成策略至关重要。不同的场景对 ID 的要求不同,本文从全局唯一性有序性安全性可读性性能等,来对比不同分布式id生成策略的优劣。

1.2、核心要求

  1. 全局唯一性:在任何时间、任何节点生成的 ID 都不能重复。
  2. 高可用/高性能:ID 生成不能成为系统瓶颈,最好无中心依赖或弱依赖。
  3. 趋势递增:在MySQL InnoDB引擎中使用的是聚集索引,由于多数RDBMS使用B-tree的数据结构来存储索引数据,在主键的选择上面我们应该尽量使用有序的主键保证写入性能。
  4. 安全性/不可预测性:如果ID是连续的,恶意用户的扒取工作就非常容易做了,直接按照顺序下载指定URL即可;如果是订单号就更危险了,竞对可以直接知道我们一天的单量。所以在一些应用场景下,会需要ID无规则、不规则。
  5. 紧凑性:ID 尽量短,节省存储和传输开销。
  6. 带时间信息 :便于排查问题、排序或做业务分析。

1.3、使用场景

  1. 微服务架构中的主键生成:每个服务独立部署,无法共享数据库自增 ID。

  2. 数据库分库分表:必须使用全局唯一 ID 作为主键,否则跨分片会冲突。

  3. 消息队列中的消息 ID: Kafka、RocketMQ 等消息中间件中,每条消息需唯一 ID 用于去重、追踪。

  4. 日志追踪:在分布式链路追踪中,每个请求需全局唯一的 Trace ID。

  5. 对外暴露的资源标识:如短链接、分享链接、邀请码,要求不可预测、防枚举。

  6. 高并发下单/支付系统:需要高性能、无锁、趋势递增的 ID

二、分布式ID生成策略

2.1、数据库自增 ID

原理:数据库在插入记录时自动分配一个递增整数作为主键。

优点十分明显:简单易用,有序,数据库来维护并发安全问题

缺点:强依赖数据库,很难进行扩容,多实例不唯一,暴露数据量,可被枚举。

2.2、UUID

原理:UUID(Universally Unique Identifier)的标准型式包含32个16进制数字,以连字号分为五段,形式为8-4-4-4-12的36个字符,示例:550e8400-e29b-41d4-a716-446655440000,到目前为止业界一共有5种方式生成UUID,详情见IETF发布的UUID规范 A Universally Unique IDentifier (UUID) URN Namespace**。 版本:

  • UUIDv1:60位时间戳 + 48 位节点 ID(硬件MAC地址)+ 14 位时钟序列
  • UUIDv4:122位密码学安全随机数 + 4 位版本号 + 2 位变体标识
  • UUIDv6/v7:基于v1保留时间顺序 + 隐藏 MAC + 兼容现有系统

优点,v6/v7有序,碰撞概率极低不依赖其它节点。

缺点:UUID太长,16字节128位,通常以36长度的字符串表示,很多场景不适用,可读性差,可能导致innodb存储引擎二级索引膨胀。

2.3、Snowflake雪花算法

原理:Twitter 开源的 64 位整数 ID 生成算法,结构如下:

01888770c8f84b1df258ddd1d424535c68559.png

优点:只需要64位就能满足,时间有序,全局唯一,高性能:12个自增序列号可以表示2^12个ID,理论上snowflake方案的QPS约为409.6w/s,这种分配方式可以保证在任何一个IDC的任何一台机器在任意毫秒内生成的ID都是不同的。。

缺点:可能出现时钟回拨问题;

2.4、号段模式

原理:从数据库批量预取一段连续 ID(如 1000~1999),本地缓存并分配,用完再申请下一段。

优点:提前申请号段应对高并发,号段可关联业务,有序。

缺点:号段大小不好分配,可能出现id浪费,依赖数据库。

2.5、Redis INCR

原理:利用 Redis 的原子操作 INCR key 生成递增整数。

优点:全局唯一,且有序,性能比MySQL高一个数量级。

缺点:依赖redis,持久化并不稳定,且延续数据库自增的缺点。

2.6、补充:

Leaf(美团)

1.Leaf-Segment模式:

  • 双 Buffer 异步加载(Segment 模式):提前加载下一段,避免请求阻塞;
  • 支持多业务隔离:通过 biz_tag 区分不同 ID 流(如 user/order);
  • 容忍短暂 DB 不可用:本地号段未耗尽前仍可发号。

2.Leaf-Snowflake模式:

  • 通过 ZooKeeper自动分配 workId,并缓存到本地文件

UIDGenerator(百度)

  • 默认结构:28bit 秒级时间戳 + 22bit workId + 13bit 序列号
  • RingBuffer 预生成 ID,后台线程填充,获取时无锁
  • 检测到回拨后主动等待,可配置最大容忍时间
  • 支持多种 workId 分配方式(DB、IP 哈希、手动)
  • 提供 Spring Boot 集成,开箱即用。

Tinyid (滴滴)

  • 类似 Leaf Segment,但更轻量,无ZookeePer依赖
  • 客户端缓存号段,快用完时异步拉取

三、总结:

1.如果你需要生成订单号、发票号等对连续性和可读性有要求的业务 ID,号段模式(如 Leaf-Segment 或 TinyID) 是首选——它既能保证趋势递增以优化数据库写入性能,又能通过业务标签灵活隔离不同 ID 流。

2.对于高并发的内部系统主键或消息 ID(如日志、事件、用户ID),Snowflake及其改进版(如 Leaf-Snowflake、UidGenerator) 更为合适,它们无中心依赖、性能极高,且天然携带时间信息,便于追踪与排序。

3.当 ID 需要对外暴露且必须防枚举、防爬取(如分享链接、邀请码、资源标识),则应放弃有序性,转而使用 UUIDv4/v7、NanoID 或 HashID 等不可预测的随机标识,并将其与内部有序 ID 做映射隔离。

4.若系统规模尚小、追求快速落地,Redis 自增也可作为轻量级过渡方案,但需警惕其单点与持久化风险。

归根结底,ID 的设计是业务需求、系统架构与安全策略的综合体现——理解场景,方能选型得当。