分布式ID生成指南:从UUID到SnowFlake

43 阅读4分钟

在数据量较小的单体服务中,通常使用单一数据库,每条记录的唯一标识由数据库自增ID保证,服务通过ID即可准确访问对应记录。而随着数据量增长,单一数据库可能成为性能瓶颈,此时会采用分库分表技术对数据库进行拆分。然而,拆分后每个数据库独立自增ID,会出现不同数据库中的记录ID重复的情况,无法满足全局唯一性。因此,分布式ID方案应运而生。

分布式ID原则

在设计分布式ID时,应该尽可能满足以下原则:

  1. 全局唯一:整个分布式系统中ID不重复。

  2. 趋势递增:有利于数据库索引性能(如B+树索引在插入时减少叶分裂)。

  3. 高性能:ID生成速度要快,避免成为系统瓶颈。

  4. 高可用:ID服务需具备容错能力,即使部分节点故障也不影响业务。

  5. 具有一定业务属性:例如可嵌入时间信息、机房信息等。

分布式ID生成方案

方案一:UUID

UUID(Universally Unique Identifier)是一组32位的16进制数字,总数为 1632=212816^{32}=2^{128}。生成的 UUID 字符串包含:时间戳、时钟序列号、变量节点号、全局唯一标识和版本号信息。

Java 示例:

// 使用 `java.util` 包中的 UUID
String uuid = UUID.randomUUID().toString();

优点

  • 本地生成,无需网络调用,性能好。

  • 全球唯一,重复概率极低。

  • 实现简单,语言内置支持。

缺点

  • 字符串存储,占用空间大。

  • 无序,不利于数据库索引。

  • 不具备业务可读性。

  • 部分版本依赖MAC地址,存在隐私风险。

方案二:数据库自增ID(单点/集群)

在分布式场景下,可单独设立一张自增ID表,通过类似以下SQL获取ID:

REPLACE INTO seq_id (biz) VALUES ('product');

其中,seq_id 为表名,该表包含 id 字段和 biz 为业务字段。当自增 ID 表存在于多数据库时,可设置不同起始值和步长避免重复:

SET @@auto_increment_offset = 1; -- 起始值
SET @@auto_increment_increment = 2; -- 步长

优点

  • 绝对递增,ID连续。

  • 实现简单,基于现有数据库。

缺点

  • 扩展性差,新增节点需人工调整(起始值)。

  • 数据库读取 id 易成为性能瓶颈。

  • 集群模式下维护复杂。

方案三:数据库号段模式

设置 ID 生成服务,将一批ID预取到内存中逐步分配,减少数据库访问,这种实现方式的表结构示例:

字段说明
id主键
biz业务类型
max_id当前最大ID
step号段长度
version乐观锁版本号

更新语句(ID生成服务在获取一批ID时执行):

UPDATE seg_id 
SET max_id = max_id + step, version = version + 1 
WHERE biz = 'product' AND version = #{version};

在起始阶段,插入一条数据,这里以商品业务为例,设置 max_id = 0, step = 1000 (ID生成服务一次取1000 个未使用过的id),biz = 'product',version = 0,当下一次获取id时可以执行:

update segment_id set max_id = 0 + 1000, version = version + 1 where version = 0 and biz = 'product';

优点

  • 数据库压力小,一次取号多次使用。

  • 可用性强,即使数据库短暂不可用,内存中仍有ID可用。

缺点

  • 当ID生成服务故障时,内存中部分ID未使用,存在小段浪费。

  • 可能暴露业务数据量(根据ID推断)。

  • 需要额外服务实现。

开源实现:滴滴 Tinyid、美团 Leaf(号段模式)。

方案四:Redis 自增

使用 INCR 或 INCRBY 命令生成自增ID:

INCR global_id
INCRBY order_id 1000

优点

  • 性能极高,基于内存操作。

  • 可集群部署,通过步长配置(INCRBY)避免重复。

缺点

  • 依赖Redis持久化,重启可能导致ID重复或跳跃。

  • 需要维护Redis集群,增加系统复杂度。

方案五:ZooKeeper 顺序节点

利用ZooKeeper的顺序持久节点生成唯一路径:

create -s /path/prefix

优点

  • 强一致性,绝对唯一。

  • 自带高可用和顺序性。

缺点

  • 性能较差,依赖磁盘和网络同步。

  • 不适合高并发ID生成场景。

方案六:雪花算法(SnowFlake)

雪花算法是分布式 ID 常用的方案,该方案广泛应用于开源社区及各企业自研ID生成服务,其生成一个64位长整型ID,结构如下:

位段长度说明
符号位1 bit恒为0,表示正数
时间戳41 bit毫秒级时间,可用约69年
机器ID10 bit5位数据中心ID + 5位工作节点ID,最多容纳1024个节点
序列号12 bit同一毫秒内的自增序列,ID序号为0~4095

优点

  • 趋势递增,利于索引。

  • 本地生成,性能高。

  • 可嵌入时间、机器信息。

缺点

  • 依赖系统时钟,时钟回拨会导致ID重复。

  • 机器ID需预先分配,扩容稍麻烦。