分布式ID生成方案

分布式ID生成方案是分布式系统中的核心问题之一,尤其是在高并发、高可用的场景下,需要保证生成的ID具备以下特性:

  1. 全局唯一性:ID在分布式系统中必须唯一。
  2. 有序性:ID最好是有序的,便于数据库索引和查询。
  3. 高性能:ID生成速度要快,不能成为系统瓶颈。
  4. 高可用性:ID生成服务必须高可用,避免单点故障。
  5. 可扩展性:支持水平扩展,适应业务增长。

以下是几种常见的分布式ID生成方案及其实现细节:


一、常见分布式ID生成方案

1. UUID

  • 原理:基于随机数生成128位的唯一标识符。
  • 优点
    • 简单易用,无需中心化服务。
    • 生成速度快。
  • 缺点
    • 无序,不适合作为数据库主键。
    • 存储空间大(36字符)。
    • 查询性能差。
  • 适用场景:小规模、非连续ID场景。
1
2
3
4
5
6
7
import java.util.UUID;

public class UUIDGenerator {
public static String generate() {
return UUID.randomUUID().toString();
}
}

2. 数据库自增ID

  • 原理:利用数据库的自增主键特性生成ID。
  • 优点
    • 简单易实现。
    • ID有序。
  • 缺点
    • 性能瓶颈,单点故障。
    • 不适合高并发场景。
  • 适用场景:小规模、低并发场景。
1
2
3
CREATE TABLE id_generator (
id BIGINT PRIMARY KEY AUTO_INCREMENT
);

3. Redis自增ID

  • 原理:利用Redis的INCR命令生成自增ID。
  • 优点
    • 高性能,支持分布式。
    • 简单易用。
  • 缺点
    • 依赖Redis,需考虑持久化和高可用。
  • 适用场景:中等规模、高并发场景。
1
2
3
4
5
6
7
8
9
10
11
12
13
14
import redis.clients.jedis.Jedis;

public class RedisIdGenerator {
private static final String ID_KEY = "global:id";
private Jedis jedis;

public RedisIdGenerator(Jedis jedis) {
this.jedis = jedis;
}

public long nextId() {
return jedis.incr(ID_KEY);
}
}

4. Snowflake算法

  • 原理:生成64位的ID,包含时间戳、机器ID和序列号。
  • 优点
    • 高性能,分布式生成。
    • ID有序。
  • 缺点
    • 依赖机器时钟,时钟回拨问题。
  • 适用场景:大规模、高并发场景。
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
public class SnowflakeIdGenerator {
private final long epoch = 1609459200000L; // 2021-01-01 00:00:00
private final long workerIdBits = 10L;
private final long sequenceBits = 12L;
private final long maxWorkerId = ~(-1L << workerIdBits);
private final long workerIdShift = sequenceBits;
private final long timestampShift = sequenceBits + workerIdBits;
private final long sequenceMask = ~(-1L << sequenceBits);

private long workerId;
private long sequence = 0L;
private long lastTimestamp = -1L;

public SnowflakeIdGenerator(long workerId) {
if (workerId > maxWorkerId || workerId < 0) {
throw new IllegalArgumentException("Worker ID超出范围");
}
this.workerId = workerId;
}

public synchronized long nextId() {
long timestamp = System.currentTimeMillis();
if (timestamp < lastTimestamp) {
throw new RuntimeException("时钟回拨异常");
}
if (timestamp == lastTimestamp) {
sequence = (sequence + 1) & sequenceMask;
if (sequence == 0) {
timestamp = tilNextMillis(lastTimestamp);
}
} else {
sequence = 0L;
}
lastTimestamp = timestamp;
return ((timestamp - epoch) << timestampShift)
| (workerId << workerIdShift)
| sequence;
}

private long tilNextMillis(long lastTimestamp) {
long timestamp = System.currentTimeMillis();
while (timestamp <= lastTimestamp) {
timestamp = System.currentTimeMillis();
}
return timestamp;
}
}

5. Leaf算法

  • 原理:美团开源的分布式ID生成服务,支持号段模式和Snowflake模式。
  • 优点
    • 高性能,支持分段缓存。
    • ID有序。
  • 缺点
    • 依赖数据库,实现复杂。
  • 适用场景:大规模、高并发场景。
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
public class LeafSegmentService {
private AtomicLong currentId;
private AtomicLong maxId;
private int step;
private String bizTag;

public LeafSegmentService(String bizTag) {
this.bizTag = bizTag;
loadSegment();
}

private void loadSegment() {
// 从数据库获取号段
IdSegment segment = idSegmentDAO.getSegment(bizTag);
currentId = new AtomicLong(segment.getMaxId() - segment.getStep());
maxId = new AtomicLong(segment.getMaxId());
step = segment.getStep();
}

public long nextId() {
long id = currentId.incrementAndGet();
if (id >= maxId.get()) {
synchronized (this) {
if (id >= maxId.get()) {
loadSegment();
id = currentId.incrementAndGet();
}
}
}
return id;
}
}

二、方案对比与选型建议

方案 优点 缺点 适用场景
UUID 简单、无需中心化服务 无序、存储空间大、查询性能差 小规模、非连续ID场景
数据库自增ID 简单、有序 性能瓶颈、单点故障 小规模、低并发场景
Redis自增ID 性能高、支持分布式 依赖Redis、持久化问题 中等规模、高并发场景
Snowflake算法 高性能、有序、分布式 依赖机器时钟、时钟回拨问题 大规模、高并发场景
Leaf算法 高性能、支持分段缓存 依赖数据库、实现复杂 大规模、高并发场景

三、性能优化建议

  1. Snowflake优化

    • 使用Zookeeper或Nacos动态分配workerId
    • 增加时钟回拨检测机制。
  2. Leaf-Segment优化

    • 增加双Buffer机制,预加载下一个号段。
    • 使用本地缓存减少数据库访问。
  3. Redis优化

    • 使用Redis集群提高可用性。
    • 设置合理的INCR步长,减少网络开销。

四、典型场景示例

1. 电商订单ID生成

  • 使用Snowflake算法,确保ID有序且高性能。
  • 业务标识嵌入workerId,便于分库分表。

2. 日志追踪ID生成

  • 使用Leaf-Segment方案,支持大范围ID分配。
  • 结合业务标识(如biz_tag)实现多业务隔离。

3. 分布式锁ID生成

  • 使用Redis自增ID,简单高效。
  • 结合EXPIRE命令实现锁超时机制。

通过以上方案,可以根据具体业务需求选择合适的分布式ID生成策略,确保系统的高性能和高可用性。