跳至主要內容

常见的全局 ID 生成方案

chenxi编程分布式大约 5 分钟

全局 ID 在分布式场景中很常见,主要目的是为了唯一标识某一事物。 例如要标识一位用户,若是采用数据库自增主键作为用户 ID,遇到分库分表这样的场景,就可能会造成用户 ID 冲突的情况。 现在主流的全局 ID 的方案主要有着如下这些:

  • UUID
  • 雪花 ID
  • 基于 Redis/ZooKeeper 进行生成

1 UUID

1.1 什么是 UUID?

UUID 是一种 128 位的全局唯一标识符,广泛应用于分布式系统中,确保 ID 的全局唯一性,UUID 的生成基于算法,因此可以在不同设备、不同系统上独立生成。

1.2 UUID 的结构

UUID 通常表示为 32 个十六进制字符,中间使用连字符分为 5 组,格式如下:

xxxxxxxx-xxxx-Mxxx-Nxxx-xxxxxxxxxxxx

x:十六进制字符(0-9 和 a-f)。

M:表示 UUID 的版本号(1-5)。

N:前两位表示 UUID 的变种(variant)。

例如:e4cc3d44-3b3a-4f6a-855c-62f2a87f27aa

UUID 因为有着 6 或 7 位用于决定 UUID 的版本和变种,所以 UUID 的随机性往往由剩下的 122 位决定。

1.3 UUID 的优缺点

UUID 有着不同的版本,不同版本的优缺点亦不相同,这里以目前用的最多的 v4 版本为例:

优点:

  • UUID 的随机性依赖于其生成算法和随机数源,122 位的随机数组合提供了 2^122 种可能性,可以说,不会出现冲突的可能。
  • 无需依赖任何外部的配置,完全的去中心化。

缺点:

  • 128 位的长度较大,增加了储存的成本。
  • 因为是完全随机,不能基于 UUID 进行排序,若是作为数据库的主键,会导致查询效率较低。

1.4 UUID 使用实例

Java 原生库中就有着 UUID 的工具类,使用起来非常简单:

UUID uuid = UUID.randomUUID();
System.out.println(uuid);

2 雪花 ID

2.1 什么是雪花 ID?

雪花 ID 是由 Twitter 提出的一个分布式唯一 ID 生成算法,能够在不依赖中心化的服务的情况下,在分布式系统中生成全局唯一且递增的 64 位 ID。 它的核心特点是生成的 ID 可以按照时间顺序排序,同时避免了在高并发场景下的冲突。

2.2 雪花 ID 的结构

雪花 ID 总共是 64 位:

  • 1 位:符号位,始终为 0。
  • 41 位:时间戳(毫秒精度),从某个固定的时间点开始计算。
  • 10 位:机器 ID,标识生成雪花 ID 的机器。
  • 12 位:序列号,通过递增的方式,用于同一机器、同一毫秒,生成不同的雪花 ID。

2.3 雪花 ID 的优缺点

优点:

  • 高效生成:因为无需依赖网络或数据库,且算法相对简单,在高并发下亦能快速生成。
  • 一定的有序性:因为高位为时间戳,对于同一机器生成的雪花 ID 可基于时间顺序进行排序。
  • 去中心化:不依赖中心服务,无需集中协调。
  • 节省储存空间:雪花 ID 为 64 位整数,占用空间相对较小。

缺点:

  • 依赖于机器:因为有着机器 ID,所以雪花 ID 的生成依赖于机器。
  • 时钟回拨问题:如果系统时间发生回退,可能会导致雪花 ID 的重复或是不按预期进行排序。

雪花 ID 如何解决时钟回拨问题?

  1. 等待模式:检测到时钟回拨时,雪花 ID 生成器进入到等待状态,直到系统时间回到回拨之前的状态。
  2. 递增序列号:为每个时刻生成的雪花 ID 分配一个递增的序列号,当检测到时钟回拨时,获取到序列号最大的雪花 ID 的时间戳,基于该时间戳进行适当调整后继续生成新的雪花 ID。

2.4 雪花 ID 使用实例

基于 hutool 工具包,封装一个雪花 ID 生成器,用于生成全局唯一的用户 ID:

import cn.hutool.core.lang.Snowflake;

public class SnowflakeIdGenerator {
    // Snowflake ID 生成器
    private static Snowflake snowflake;

    // 初始化 Snowflake 生成器,传入机器 ID 和 数据中心 ID
    static {
        snowflake = new Snowflake(1L, 1L);
    }

    // 生成用户 ID
    public static long generateUserId() {
        return snowflake.nextId();
    }
}

3 基于 Redis/ZooKeeper 进行生成

相比 UUID 与 雪花 ID,基于 Redis/ZooKeeper 进行生成的方案是一种中心化的方案,依赖于中间件 Redis 或是 ZooKeeper。

核心思想就是利用到了 Redis 的 string 类型的自增的特点,而对于 ZooKeeper 则是使用到了其顺序节点的特性。

若是项目中本身就使用到了 Redis 或 ZooKeeper,使用这种方案简单直接,且能保证全局的顺序性(这是 UUID 和雪花 ID 所做不到的)

但因为依赖于中间件,所以 ID 的生成会产生一定的网络开销,对网络延迟敏感,且一旦 Redis 或 ZooKeeper 故障,ID 就将无法生成。

4 总结

UUID 适用场景:

  • 完全的去中心化
  • 对 ID 生成的性能与 ID 所占储存空间无太大要求
  • 不要求 ID 的有序性

雪花 ID 适用场景:

  • 去中心化但依赖机器 ID
  • 对 ID 生成的性能与 ID 所占储存空间有一定要求
  • 要求 ID 具有一定的有序性以提高数据库查询效率

Redis/ZooKeeper 生成全局 ID 适用场景:

  • 中心化,复用中间件
  • 要求保证 ID 的全局顺序性
  • 在性能上能接受网络开销
上次编辑于: