在数据量较小的单体服务中,通常使用单一数据库,每条记录的唯一标识由数据库自增ID保证,服务通过ID即可准确访问对应记录。而随着数据量增长,单一数据库可能成为性能瓶颈,此时会采用分库分表技术对数据库进行拆分。然而,拆分后每个数据库独立自增ID,会出现不同数据库中的记录ID重复的情况,无法满足全局唯一性。因此,分布式ID方案应运而生。
分布式ID原则
在设计分布式ID时,应该尽可能满足以下原则:
-
全局唯一:整个分布式系统中ID不重复。
-
趋势递增:有利于数据库索引性能(如B+树索引在插入时减少叶分裂)。
-
高性能:ID生成速度要快,避免成为系统瓶颈。
-
高可用:ID服务需具备容错能力,即使部分节点故障也不影响业务。
-
具有一定业务属性:例如可嵌入时间信息、机房信息等。
分布式ID生成方案
方案一:UUID
UUID(Universally Unique Identifier)是一组32位的16进制数字,总数为 。生成的 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年 |
| 机器ID | 10 bit | 5位数据中心ID + 5位工作节点ID,最多容纳1024个节点 |
| 序列号 | 12 bit | 同一毫秒内的自增序列,ID序号为0~4095 |
优点
-
趋势递增,利于索引。
-
本地生成,性能高。
-
可嵌入时间、机器信息。
缺点
-
依赖系统时钟,时钟回拨会导致ID重复。
-
机器ID需预先分配,扩容稍麻烦。