Skip to content

分布式 ID

要求

  1. 唯一性:全局唯一
  2. 趋势递增:趋势递增有利于索引
  3. 高可用:服务高可用
  4. 高性能: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();

总结

  1. UUID:无序、字符串、简单场景
  2. 数据库自增:强依赖数据库
  3. Redis INCR:性能高、依赖 Redis
  4. 雪花算法:主流方案、有序、高性能

基于 VitePress 构建