Java 雪花 ID 生成器

在分布式系统中,生成唯一标识符(ID)是个常见的需求。传统的自增长 ID 在分布式环境中可能会产生冲突。为了解决这个问题,Twitter 提出的雪花(Snowflake)算法应运而生,它可以高效地生成全球唯一的 64 位整数 ID。本文将介绍 Java 中的雪花 ID 生成器,并提供相关的代码示例。

雪花 ID 生成器原理

雪花 ID 一般由五部分组成:

  1. 符号位(1 位):始终为 0。
  2. 时间戳(41 位):当前时间戳,单位为毫秒。在 69 年后需要更换算法。
  3. 机器 ID(10 位):分布式系统中不同节点的唯一标识,可以根据机器ID和数据中心ID组合。
  4. 序列号(12 位):同一毫秒内生成的 ID 序列号,避免重复。

这样,雪花 ID 具有高并发、高可用性和唯一性的特点。

Java 实现

接下来我们将详细介绍如何用 Java 实现一个雪花 ID 生成器。

代码示例

下面是一个简单的雪花 ID 生成器的实现:

public class SnowflakeIdGenerator {
    // 起始时间戳
    private final long epoch = 1577836800000L; // 2020-01-01 00:00:00
    // 机器ID位数
    private final long machineIdBits = 5L;    // 32个节点
    // 数据中心ID位数
    private final long datacenterIdBits = 5L; // 32个中心
    // 最大机器ID
    private final long maxMachineId = -1L ^ (-1L << machineIdBits);
    // 最大数据中心ID
    private final long maxDatacenterId = -1L ^ (-1L << datacenterIdBits);
    // 序列号位数
    private final long sequenceBits = 12L;     // 每毫秒4096个
    // 机器ID位偏移
    private final long machineIdShift = sequenceBits;
    // 数据中心ID位偏移
    private final long datacenterIdShift = sequenceBits + machineIdBits;
    // 时间戳位偏移
    private final long timestampLeftShift = sequenceBits + machineIdBits + datacenterIdBits;

    private long lastTimestamp = -1L;
    private long sequence = 0L;
    private final long machineId;
    private final long datacenterId;

    public SnowflakeIdGenerator(long machineId, long datacenterId) {
        if (machineId > maxMachineId || machineId < 0) {
            throw new IllegalArgumentException("Machine ID can't be greater than " + maxMachineId + " or less than 0");
        }
        if (datacenterId > maxDatacenterId || datacenterId < 0) {
            throw new IllegalArgumentException("Datacenter ID can't be greater than " + maxDatacenterId + " or less than 0");
        }
        this.machineId = machineId;
        this.datacenterId = datacenterId;
    }

    public synchronized long nextId() {
        long timestamp = System.currentTimeMillis();

        if (timestamp < lastTimestamp) {
            throw new RuntimeException("Timestamp is moving backwards. Rejecting requests until " + lastTimestamp);
        }

        if (lastTimestamp == timestamp) {
            sequence = (sequence + 1) & (1L << sequenceBits - 1); // 12位序列号
        } else {
            sequence = 0L; // 时间戳改变,重置序列号
        }

        lastTimestamp = timestamp;

        // 形成最终的ID
        return ((timestamp - epoch) << timestampLeftShift) 
                | (datacenterId << datacenterIdShift) 
                | (machineId << machineIdShift) 
                | sequence;
    }
}

使用示例

下面是如何使用 SnowflakeIdGenerator 类生成 ID:

public class Main {
    public static void main(String[] args) {
        // 创建雪花 ID 生成器,假设机器ID为1,数据中心ID为1
        SnowflakeIdGenerator generator = new SnowflakeIdGenerator(1, 1);
        
        // 生成ID
        long id = generator.nextId();
        System.out.println("生成的ID: " + id);
    }
}

类图

下面是雪花 ID 生成器的类图:

classDiagram
    class SnowflakeIdGenerator {
        +long epoch
        +long machineId
        +long datacenterId
        +long nextId()
    }

总结

雪花 ID 生成器是一个高效、简洁并且具备容错能力的 ID 生成解决方案,尤其适合用于分布式系统。在设计分布式应用时,独特的 ID 生成方式可以避免数据冲突,提高系统的可用性和扩展性。希望本文对了解雪花 ID 生成器有所帮助,鼓励读者在自己的项目中进行尝试!