雪花算法:分布式系统唯一ID生成算法

2023-09-19 08:45:30 浏览数 (3)

一、由来

雪花算法(Snowflake Algorithm)是一种用于生成分布式系统中唯一ID的算法。起初由Twitter设计,用于解决分布式系统中唯一ID的需求。这一算法的目标是生成全局唯一、有序的64位整数ID,以确保数据不冲突、不重复。

二、结构

雪花算法生成的ID由以下部分组成:

  • 41位时间戳:精确到毫秒级,记录ID生成的时间。
  • 10位机器ID:用于标识不同机器或节点,可自定义分配。
  • 12位序列号:解决同一毫秒内生成多个ID的冲突问题。

三、工作原理

雪花算法的工作原理如下:

  1. 获取当前时间戳的41位,并将其左移22位,以获取时间戳部分。
  2. 获取机器ID,并将其左移12位,以获取机器ID部分。
  3. 生成序列号,同一毫秒内生成多个ID时,序列号递增。
  4. 合并时间戳、机器ID和序列号,形成64位整数ID。

四、应用场景与特点

雪花算法广泛应用于分布式系统,用于生成唯一标识符,如订单号、用户ID、消息ID等。在解决分布式系统中唯一ID生成需求时非常有效,已被众多大型互联网公司和开源项目采用。

主要有以下特点:

  • 唯一性:雪花算法生成的ID在分布式环境下全局唯一,不会重复。
  • 有序性:生成的ID有序,可根据ID大小排序。
  • 高性能:ID生成速度快,适用于高并发的分布式系统。
  • 可定制性:机器ID可自定义,适用于各种规模和需求的系统

五、Java代码实现

工具类:

代码语言:javascript复制
package com.forum.core.Utils.Snowflake;

public class SnowflakeIdGenerator {
    // ==============================字段==============================
    private final long workerId;          // 机器ID
    private final long datacenterId;      // 数据中心ID
    private long sequence = 0L;           // 序列号

    // 配置参数
    private static final long MAX_WORKER_ID = 31L;
    private static final long MAX_DATACENTER_ID = 31L;
    private static final long TIMESTAMP_BITS = 41L;
    private static final long MAX_TIMESTAMP = ~(-1L << TIMESTAMP_BITS);
    private static final long WORKER_ID_BITS = 5L;
    private static final long DATACENTER_ID_BITS = 5L;
    private static final long SEQUENCE_BITS = 12L;

    // 位移偏移量
    private static final long TIMESTAMP_SHIFT = SEQUENCE_BITS   WORKER_ID_BITS   DATACENTER_ID_BITS;
    private static final long WORKER_ID_SHIFT = SEQUENCE_BITS   DATACENTER_ID_BITS;
    private static final long DATACENTER_ID_SHIFT = SEQUENCE_BITS;

    private long lastTimestamp = -1L;     // 上次生成ID的时间戳

    // ==============================构造函数==============================
    /**
     * 构造函数
     * @param workerId     机器ID (0 - 31)
     * @param datacenterId 数据中心ID (0 - 31)
     */
    public SnowflakeIdGenerator(long workerId, long datacenterId) {
        // 检查workerId和datacenterId是否合法
        if (workerId > MAX_WORKER_ID || workerId < 0) {
            throw new IllegalArgumentException("Worker ID must be between 0 and "   MAX_WORKER_ID);
        }
        if (datacenterId > MAX_DATACENTER_ID || datacenterId < 0) {
            throw new IllegalArgumentException("Datacenter ID must be between 0 and "   MAX_DATACENTER_ID);
        }

        // 设置workerId和datacenterId
        this.workerId = workerId;
        this.datacenterId = datacenterId;
    }

    // ==============================方法==============================
    /**
     * 生成唯一ID
     * @return 唯一ID
     */
    public synchronized long generateUniqueId() {
        long timestamp = getCurrentTimestamp();

        // 检查时钟回退情况
        if (timestamp < lastTimestamp) {
            throw new RuntimeException("Clock moved backwards. Refusing to generate ID.");
        }

        // 同一毫秒内自增序列号
        if (timestamp == lastTimestamp) {
            sequence = (sequence   1) & ((1 << SEQUENCE_BITS) - 1);  // 自增并掩码
            if (sequence == 0) {
                // 序列号用完,等待下一毫秒
                timestamp = getNextTimestamp(lastTimestamp);
            }
        } else {
            sequence = 0L;  // 不同毫秒重置序列号
        }

        lastTimestamp = timestamp;

        // 组合生成唯一ID
        return ((timestamp << TIMESTAMP_SHIFT) |
                (datacenterId << DATACENTER_ID_SHIFT) |
                (workerId << WORKER_ID_SHIFT) |
                sequence);
    }

    /**
     * 获取当前时间戳
     * @return 当前时间戳(毫秒)
     */
    private long getCurrentTimestamp() {
        return System.currentTimeMillis();
    }

    /**
     * 等待下一毫秒,直到获得新的时间戳
     * @param lastTimestamp 上次生成ID的时间戳
     * @return 新的时间戳
     */
    private long getNextTimestamp(long lastTimestamp) {
        long timestamp = getCurrentTimestamp();
        while (timestamp <= lastTimestamp) {
            timestamp = getCurrentTimestamp();
        }
        return timestamp;
    }

    // ==============================测试==============================
    public static void main(String[] args) {
        // 创建一个雪花算法生成器,传入机器ID和数据中心ID
        SnowflakeIdGenerator idGenerator = new SnowflakeIdGenerator(0, 0);
        // 生成10个唯一ID并打印
        for (int i = 0; i < 10; i  ) {
            new Thread(()->{
                long uniqueId = idGenerator.generateUniqueId();
                System.out.println("Generated Unique ID: "   uniqueId);
            }).start();
        }
    }
}

测试输出:

代码语言:javascript复制
Generated Unique ID: 7109423425210286080
Generated Unique ID: 7109423425214480385
Generated Unique ID: 7109423425214480384
Generated Unique ID: 7109423425210286081
Generated Unique ID: 7109423425214480389
Generated Unique ID: 7109423425214480388
Generated Unique ID: 7109423425214480387
Generated Unique ID: 7109423425214480386
Generated Unique ID: 7109423425214480391
Generated Unique ID: 7109423425214480390

六、总结

雪花算法是一种为分布式系统生成唯一ID的算法,具备唯一性、有序性和高性能等特点,适用于各类分布式系统。通过合理分配机器ID和序列号,满足不同规模和需求的系统,成为分布式系统中常用的ID生成方案。

0 人点赞