分布式ID生成方案是分布式系统中的核心问题之一,尤其是在高并发、高可用的场景下,需要保证生成的ID具备以下特性:
- 全局唯一性:ID在分布式系统中必须唯一。
- 有序性:ID最好是有序的,便于数据库索引和查询。
- 高性能:ID生成速度要快,不能成为系统瓶颈。
- 高可用性:ID生成服务必须高可用,避免单点故障。
- 可扩展性:支持水平扩展,适应业务增长。
以下是几种常见的分布式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。
- 优点:
- 缺点:
- 适用场景:小规模、低并发场景。
1 2 3
| CREATE TABLE id_generator ( id BIGINT PRIMARY KEY AUTO_INCREMENT );
|
3. Redis自增ID
- 原理:利用Redis的
INCR
命令生成自增ID。
- 优点:
- 缺点:
- 适用场景:中等规模、高并发场景。
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和序列号。
- 优点:
- 缺点:
- 适用场景:大规模、高并发场景。
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; 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模式。
- 优点:
- 缺点:
- 适用场景:大规模、高并发场景。
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算法 |
高性能、支持分段缓存 |
依赖数据库、实现复杂 |
大规模、高并发场景 |
三、性能优化建议
Snowflake优化:
- 使用Zookeeper或Nacos动态分配
workerId
。
- 增加时钟回拨检测机制。
Leaf-Segment优化:
- 增加双Buffer机制,预加载下一个号段。
- 使用本地缓存减少数据库访问。
Redis优化:
- 使用Redis集群提高可用性。
- 设置合理的
INCR
步长,减少网络开销。
四、典型场景示例
1. 电商订单ID生成
- 使用Snowflake算法,确保ID有序且高性能。
- 业务标识嵌入
workerId
,便于分库分表。
2. 日志追踪ID生成
- 使用Leaf-Segment方案,支持大范围ID分配。
- 结合业务标识(如
biz_tag
)实现多业务隔离。
3. 分布式锁ID生成
- 使用Redis自增ID,简单高效。
- 结合
EXPIRE
命令实现锁超时机制。
通过以上方案,可以根据具体业务需求选择合适的分布式ID生成策略,确保系统的高性能和高可用性。