分布式ID雪花算法生成ID

wen java案例 2

本文目录导读:

分布式ID雪花算法生成ID

  1. 雪花算法简介
  2. ID结构组成
  3. 核心代码实现
  4. 性能优化版本
  5. 应用示例
  6. 优缺点分析
  7. 改进方案
  8. 最佳实践

我来详细讲解分布式ID雪花算法(Snowflake Algorithm)的原理和实现。

雪花算法简介

雪花算法是Twitter开源的分布式ID生成算法,用于在分布式系统中生成唯一的、有序的64位整数ID。

ID结构组成

 1 bit  |  41 bits  |  10 bits  |  12 bits
--------|-----------|-----------|-----------
 符号位  |  时间戳   |  工作机器  |  序列号
   (0)  | (毫秒级)  |   ID      |   (自增)

各字段说明:

  • 符号位(1bit):始终为0,保证ID为正数
  • 时间戳(41bit):毫秒级时间戳,可使用69年
  • 机器ID(10bit):支持1024台机器
  • 序列号(12bit):同一毫秒内可生成4096个ID

核心代码实现

public class SnowflakeIdGenerator {
    // 起始时间戳 (2021-01-01 00:00:00)
    private final static long START_TIME = 1609459200000L;
    // 各部分位数
    private final static long WORKER_ID_BITS = 10L;
    private final static long SEQUENCE_BITS = 12L;
    // 最大值
    private final static long MAX_WORKER_ID = ~(-1L << WORKER_ID_BITS);
    private final static long MAX_SEQUENCE = ~(-1L << SEQUENCE_BITS);
    // 左移位数
    private final static long WORKER_ID_SHIFT = SEQUENCE_BITS;
    private final static long TIMESTAMP_SHIFT = SEQUENCE_BITS + WORKER_ID_BITS;
    private long workerId;
    private long sequence = 0L;
    private long lastTimestamp = -1L;
    public SnowflakeIdGenerator(long workerId) {
        if (workerId > MAX_WORKER_ID || workerId < 0) {
            throw new IllegalArgumentException(String.format(
                "Worker ID must be between 0 and %d", MAX_WORKER_ID));
        }
        this.workerId = workerId;
    }
    // 生成下一个ID
    public synchronized long nextId() {
        long timestamp = System.currentTimeMillis();
        // 时间回拨处理
        if (timestamp < lastTimestamp) {
            throw new RuntimeException(String.format(
                "Clock moved backwards. Refusing to generate id for %d milliseconds",
                lastTimestamp - timestamp));
        }
        // 同一毫秒内,序列号自增
        if (timestamp == lastTimestamp) {
            sequence = (sequence + 1) & MAX_SEQUENCE;
            // 序列号用完,等待下一毫秒
            if (sequence == 0) {
                timestamp = waitNextMillis(timestamp);
            }
        } else {
            // 不同毫秒,序列号重置
            sequence = 0L;
        }
        lastTimestamp = timestamp;
        // 组合各部分生成ID
        return ((timestamp - START_TIME) << TIMESTAMP_SHIFT) 
             | (workerId << WORKER_ID_SHIFT) 
             | sequence;
    }
    // 等待下一毫秒
    private long waitNextMillis(long currentTimestamp) {
        long timestamp = System.currentTimeMillis();
        while (timestamp <= currentTimestamp) {
            timestamp = System.currentTimeMillis();
        }
        return timestamp;
    }
}

性能优化版本

public class OptimizedSnowflakeIdGenerator {
    private static final long START_TIME = 1609459200000L;
    private static final long BITS_SEQUENCE = 12;
    private static final long BITS_MACHINE = 10;
    private static final long MASK_SEQUENCE = ~(-1L << BITS_SEQUENCE);
    private static final long MAGIC_NUMBER = 1 << (BITS_MACHINE + BITS_SEQUENCE);
    private final long machineId;
    private volatile long lastTimestamp = -1L;
    private volatile long sequence = 0L;
    // 预置好的机器ID部分
    private final long machineIdPart;
    public OptimizedSnowflakeIdGenerator(long machineId) {
        this.machineId = machineId;
        this.machineIdPart = machineId << BITS_SEQUENCE;
    }
    public long nextId() {
        long timestamp = System.currentTimeMillis();
        // 处理时钟回拨
        if (timestamp < lastTimestamp) {
            long offset = lastTimestamp - timestamp;
            if (offset <= 5) {
                // 小范围回拨,等待
                timestamp = waitUntil(timestamp);
            } else {
                throw new IllegalStateException("Clock moved backwards");
            }
        }
        if (timestamp == lastTimestamp) {
            sequence = (sequence + 1) & MASK_SEQUENCE;
            if (sequence == 0) {
                timestamp = waitUntil(timestamp);
            }
        } else {
            sequence = 0L;
        }
        lastTimestamp = timestamp;
        // 快速生成ID
        return ((timestamp - START_TIME) * MAGIC_NUMBER) 
             + machineIdPart 
             + sequence;
    }
    private long waitUntil(long currentTimestamp) {
        long timestamp = System.currentTimeMillis();
        while (timestamp <= currentTimestamp) {
            timestamp = System.currentTimeMillis();
        }
        return timestamp;
    }
}

应用示例

public class SnowflakeDemo {
    public static void main(String[] args) {
        // 创建生成器(机器ID范围0-1023)
        SnowflakeIdGenerator generator = new SnowflakeIdGenerator(1);
        // 批量生成ID
        for (int i = 0; i < 10; i++) {
            long id = generator.nextId();
            System.out.println("Generated ID: " + id);
        }
        // 测试性能
        long startTime = System.currentTimeMillis();
        int count = 1000000;
        for (int i = 0; i < count; i++) {
            generator.nextId();
        }
        long endTime = System.currentTimeMillis();
        System.out.println("生成 " + count + " 个ID耗时: " 
            + (endTime - startTime) + "ms");
        System.out.println("每秒生成: " + (count * 1000 / (endTime - startTime)) + " 个");
    }
}

优缺点分析

✅ 优点

  • 全局唯一:分布式环境下保证ID唯一性
  • 趋势递增:整体呈递增趋势,有利于数据库索引
  • 高性能:纯内存计算,单机百万级/秒
  • 业务含义:可反解析出生成时间、机器等信息

❌ 缺点

  • 依赖机器时钟:时钟回拨可能导致ID重复
  • 有限容量:单机每秒最多生成409.6万个(4096*1000)

改进方案

1 时钟回拨处理

// 方案1:等待时钟追上
private long handleClockBackwards(long lastTimestamp) {
    long currentTimestamp = System.currentTimeMillis();
    while (currentTimestamp < lastTimestamp) {
        currentTimestamp = System.currentTimeMillis();
    }
    return currentTimestamp;
}
// 方案2:使用备用序列号
private long handleClockBackwardsWithBackup(long lastTimestamp, long sequence) {
    if (sequence > 0) {
        // 使用前一毫秒的序列号继续生成
        this.sequence = (sequence + 1) & MAX_SEQUENCE;
        return lastTimestamp;
    }
    // 等待时钟追上
    return waitForClock(lastTimestamp);
}

2 扩展版本(百亿级ID)

public class ExtendedSnowflakeIdGenerator {
    // 增加时间戳位数到51位
    private static final long TIMESTAMP_BITS = 51L;
    // 减少机器位和序列号位
    private static final long WORKER_ID_BITS = 8L;
    private static final long SEQUENCE_BITS = 4L;
    // ... 实现逻辑类似,但支持更长时间
}

最佳实践

  1. 机器ID管理:通过Zookeeper或Redis统一分配
  2. 时钟同步:使用NTP服务同步时钟,避免大幅回拨
  3. 高可用部署:多个ID生成节点,通过负载均衡使用

雪花算法是分布式系统中ID生成的首选方案,理解其原理对系统设计很重要。

抱歉,评论功能暂时关闭!