分布式 ID
要求
- 唯一性:全局唯一
- 趋势递增:趋势递增有利于索引
- 高可用:服务高可用
- 高性能:QPS 高
方案对比
| 方案 | 优点 | 缺点 |
|---|---|---|
| UUID | 无需第三方服务 | 无序、占用空间大 |
| 数据库自增 | 简单 | 依赖单库,有瓶颈 |
| Redis INCR | 性能高 | 依赖 Redis |
| 雪花算法 | 高性能、有序 | 依赖时钟 |
| 百度 UidGenerator | 支持多种模式 | 实现复杂 |
| 滴滴 Leaf | 双保险 | 依赖 ZooKeeper |
UUID
java
// Java 原生
String uuid = UUID.randomUUID().toString();
// 格式:550e8400-e29b-41d4-a716-446655440000
// 无横线
String uuid = UUID.randomUUID().toString().replace("-", "");
// 截取
String shortId = uuid.substring(0, 16);缺点:无序、字符串长、无业务含义
数据库自增
sql
CREATE TABLE sequence (
id BIGINT PRIMARY KEY AUTO_INCREMENT,
stub VARCHAR(1) UNIQUE
);
-- 获取 ID
SELECT id FROM sequence WHERE stub = 'A';
INSERT INTO sequence(stub) VALUES('A');缺点:强依赖数据库、无法水平扩展
Redis INCR
java
@Component
public class RedisIdGenerator {
@Autowired
private RedisTemplate<String, String> redisTemplate;
public long nextId(String keyPrefix) {
// 时间戳
long timestamp = System.currentTimeMillis() / 1000;
// 序列号
String key = "id:" + keyPrefix + ":" + timestamp;
long id = redisTemplate.opsForValue().increment(key);
// 设置过期时间(1天)
redisTemplate.expire(key, 1, TimeUnit.DAYS);
return timestamp << 32 | id;
}
}雪花算法 (Snowflake)
┌────────────────────────────────────────────────────────┐
│ 64 bits │
├─────────┬───────────┬───────────────┬──────────────────┤
│ 符号位 │ 时间戳 │ 机器 ID │ 序列号 │
│ 1 bit │ 41 bits │ 10 bits │ 12 bits │
└─────────┴───────────┴───────────────┴──────────────────┘java
public class SnowflakeIdGenerator {
private final long twepoch = 1609459200000L; // 2021-01-01
private final long workerIdBits = 10L;
private final long maxWorkerId = ~(-1L << workerIdBits);
private final long sequenceBits = 12L;
private final long workerIdShift = sequenceBits;
private final long timestampLeftShift = 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 = timeGen();
if (timestamp < lastTimestamp) {
throw new RuntimeException("时钟回拨");
}
if (lastTimestamp == timestamp) {
sequence = (sequence + 1) & sequenceMask;
if (sequence == 0) {
timestamp = tilNextMillis(lastTimestamp);
}
} else {
sequence = 0L;
}
lastTimestamp = timestamp;
return ((timestamp - twepoch) << timestampLeftShift)
| (workerId << workerIdShift)
| sequence;
}
protected long tilNextMillis(long lastTimestamp) {
long timestamp = timeGen();
while (timestamp <= lastTimestamp) {
timestamp = timeGen();
}
return timestamp;
}
protected long timeGen() {
return System.currentTimeMillis();
}
}Hutool 雪花算法
xml
<dependency>
<groupId>cn.hutool</groupId>
<artifactId>hutool-core</artifactId>
<version>5.8.22</version>
</dependency>java
// 默认雪花算法
Snowflake snowflake = new Snowflake(1, 1);
long id = snowflake.nextId();
// 连续 ID(性能更好)
Sequence sequence = new Sequence();
long id = sequence.nextId();总结
- UUID:无序、字符串、简单场景
- 数据库自增:强依赖数据库
- Redis INCR:性能高、依赖 Redis
- 雪花算法:主流方案、有序、高性能
