雪花id

/**
 * 
 */
package cn.com.chengtech.project.pm.worktask;

/**
 * @author 
 * @date 2021年1月29日
 */
public class SnowFlake
{
	
	/**
     * 起始的时间戳
     */
    private final static long START_STMP = 1480166465631L;

    /**
     * 每一部分占用的位数
     */
    private final static long SEQUENCE_BIT = 12; //序列号占用的位数
    private final static long MACHINE_BIT = 5;   //机器标识占用的位数
    private final static long DATACENTER_BIT = 5;//数据中心占用的位数

    /**
     * 每一部分的最大值
     */
    private final static long MAX_DATACENTER_NUM = -1L ^ (-1L << DATACENTER_BIT);
    private final static long MAX_MACHINE_NUM = -1L ^ (-1L << MACHINE_BIT);
    private final static long MAX_SEQUENCE = -1L ^ (-1L << SEQUENCE_BIT);

    /**
     * 每一部分向左的位移
     */
    private final static long MACHINE_LEFT = SEQUENCE_BIT;
    private final static long DATACENTER_LEFT = SEQUENCE_BIT + MACHINE_BIT;
    private final static long TIMESTMP_LEFT = DATACENTER_LEFT + DATACENTER_BIT;

    private long datacenterId;  //数据中心
    private long machineId;     //机器标识
    private long sequence = 0L; //序列号
    private long lastStmp = -1L;//上一次时间戳

    public SnowFlake(long datacenterId, long machineId) {
        if (datacenterId > MAX_DATACENTER_NUM || datacenterId < 0) {
            throw new IllegalArgumentException("datacenterId can't be greater than MAX_DATACENTER_NUM or less than 0");
        }
        if (machineId > MAX_MACHINE_NUM || machineId < 0) {
            throw new IllegalArgumentException("machineId can't be greater than MAX_MACHINE_NUM or less than 0");
        }
        this.datacenterId = datacenterId;
        this.machineId = machineId;
    }

    /**
     * 产生下一个ID
     *
     * @return
     */
    public synchronized long nextId() {
        long currStmp = getNewstmp();
        if (currStmp < lastStmp) {
            throw new RuntimeException("Clock moved backwards.  Refusing to generate id");
        }

        if (currStmp == lastStmp) {
            //相同毫秒内,序列号自增
            sequence = (sequence + 1) & MAX_SEQUENCE;
            //同一毫秒的序列数已经达到最大
            if (sequence == 0L) {
                currStmp = getNextMill();
            }
        } else {
            //不同毫秒内,序列号置为0
            sequence = 0L;
        }

        lastStmp = currStmp;

        return (currStmp - START_STMP) << TIMESTMP_LEFT //时间戳部分
                | datacenterId << DATACENTER_LEFT       //数据中心部分
                | machineId << MACHINE_LEFT             //机器标识部分
                | sequence;                             //序列号部分
    }

    private long getNextMill() {
        long mill = getNewstmp();
        while (mill <= lastStmp) {
            mill = getNewstmp();
        }
        return mill;
    }

    private long getNewstmp() {
        return System.currentTimeMillis();
    }

    public static void main(String[] args) {
        SnowFlake snowFlake = new SnowFlake(1, 1);

        long start = System.currentTimeMillis();
        for (int i = 0; i < 1000000; i++) {
            System.out.println(snowFlake.nextId());
        }

        System.out.println(System.currentTimeMillis() - start);


    }
}

 

<think>好的,用户让我介绍一下雪花ID。首先,我需要回忆一下雪花ID的基本概念。雪花ID是Twitter开源的一种分布式ID生成算法,用于在分布式系统中生成唯一ID。用户可能是在开发分布式系统时遇到了ID生成的问题,或者想了解如何保证ID的唯一性和有序性。 接下来,我需要确认用户的需求层次。他们可能只是需要基础介绍,或者更深入的实现细节,比如结构组成、如何保证唯一性、优缺点等。此外,用户可能关心雪花ID在不同编程语言中的实现,或者与其他ID生成方案(如UUID、数据库自增ID)的比较。 然后,我要考虑用户的背景。如果是开发者,可能需要技术细节和代码示例;如果是非技术人员,可能需要更通俗的解释。但根据之前的对话历史,用户询问了JDK21的if-else,说明可能有Java开发背景,因此可以适当加入Java实现的示例。 还需要注意雪花ID的结构,通常是时间戳、工作节点ID、序列号三个部分。需要解释每个部分的作用,以及如何组合在一起保证ID的唯一性和递增性。同时,要提到雪花ID的优点,如高性能、有序性,以及潜在的问题,比如时钟回拨的解决方案。 另外,用户可能想知道雪花ID的应用场景,比如在分布式系统、数据库分片等环境下的使用。可能还需要对比其他ID生成方案,说明雪花ID的适用场景和限制。 最后,要确保回答结构清晰,分点说明,帮助用户逐步理解。可能还需要提醒用户注意工作节点ID的管理,避免重复,以及如何处理时钟回拨的问题,提供一些最佳实践。</think>### 🌟 **雪花ID(Snowflake ID)详解** 雪花ID是Twitter开源的一种**分布式唯一ID生成算法**,专为解决分布式系统中全局唯一ID生成问题而设计。其核心特点是:**高性能、趋势递增、可解析**,广泛应用于分布式系统、数据库分片等场景。 --- ### 1️⃣ **核心结构解析** 一个标准的64位雪花ID由以下三部分组成(以经典实现为例): $$ \underbrace{0}_{1\text{bit}}|\underbrace{0000\cdots0000}_{41\text{bit(时间戳)}}|\underbrace{00000}_{5\text{bit(数据中心ID)}}|\underbrace{00000}_{5\text{bit(机器ID)}}|\underbrace{0000\cdots0000}_{12\text{bit(序列号)}} $$ | **组成部分** | **位数** | **作用** | **示例值范围** | |---------------|----------|------------------------------------------|-------------------------| | **符号位** | 1 bit | 固定为0(保证ID为正数) | 0 | | **时间戳** | 41 bit | 毫秒级时间差(自定义起始时间) | $2^{41}/(365 \times 24 \times 3600 \times 1000) \approx 69年$ | | **数据中心ID**| 5 bit | 支持最多 $2^5=32$ 个数据中心 | 0~31 | | **机器ID** | 5 bit | 每个数据中心支持最多 $2^5=32$ 台机器 | 0~31 | | **序列号** | 12 bit | 同一毫秒内最多生成 $2^{12}=4096$ 个ID | 0~4095 | --- ### 2️⃣ **核心优势** #### ✅ **全局唯一性** - 通过**数据中心ID + 机器ID**唯一标识生成节点 - 同一毫秒内通过**序列号自增**避免冲突 #### ✅ **趋势递增** - 高位时间戳保证ID整体随时间递增,利于数据库索引优化 #### ✅ **高性能** - 纯本地计算,无需网络请求(对比UUID、数据库自增ID等方案) #### ✅ **可解析性** - 可通过位运算反向解析出生成时间、数据中心、机器等信息 --- ### 3️⃣ **Java实现示例** ```java public class SnowflakeIdGenerator { private final long datacenterId; // 数据中心ID private final long machineId; // 机器ID private long sequence = 0L; // 序列号 private long lastTimestamp = -1L; // 上次生成时间 // 时间戳起始点(可自定义,如2024-01-01) private final long epoch = 1704067200000L; public synchronized long nextId() { long currentTimestamp = System.currentTimeMillis(); if (currentTimestamp < lastTimestamp) { throw new RuntimeException("时钟回拨!"); } if (currentTimestamp == lastTimestamp) { sequence = (sequence + 1) & 0xFFF; // 12位序列号 if (sequence == 0) { // 当前毫秒序列号用尽,等待下一毫秒 currentTimestamp = waitNextMillis(currentTimestamp); } } else { sequence = 0L; } lastTimestamp = currentTimestamp; return ((currentTimestamp - epoch) << 22) // 时间戳左移22位 | (datacenterId << 17) // 数据中心左移17位 | (machineId << 12) // 机器ID左移12位 | sequence; // 序列号 } private long waitNextMillis(long currentTimestamp) { while (currentTimestamp <= lastTimestamp) { currentTimestamp = System.currentTimeMillis(); } return currentTimestamp; } } ``` --- ### 4️⃣ **关键问题与解决方案** #### 🔥 **时钟回拨问题** - **现象**:服务器时钟突然回调导致可能生成重复ID - **解决方案**: 1. 短时间回拨(<100ms):等待时钟追平 2. 长时间回拨:记录异常日志并报警 #### 🔥 **ID分配管理** - **数据中心/机器ID分配**:需通过配置中心或数据库统一管理,避免重复 - **动态扩容**:可通过增加位数或使用ZooKeeper等协调服务动态分配 --- ### 5️⃣ **对比其他ID方案** | **方案** | **唯一性** | **有序性** | **长度** | **缺点** | |-------------------|------------------|------------|----------|---------------------------| | **雪花ID** | 全局唯一 | 趋势递增 | 64 bit | 依赖时钟同步 | | **UUID** | 全局唯一 | 无序 | 128 bit | 索引分裂问题 | | **数据库自增ID** | 单库唯一 | 严格递增 | 64 bit | 扩展性差、有性能瓶颈 | | **Redis INCR** | 全局唯一 | 严格递增 | 64 bit | 依赖网络、增加系统复杂度 | --- ### 6️⃣ **最佳实践** 1. **数据中心/机器ID管理**:使用配置中心(如Nacos)动态分配 2. **时钟同步**:部署NTP服务保证服务器时间同步 3. **监控报警**:对时钟回拨、序列号耗尽等情况设置监控 4. **扩展改进**: - 百度UIDGenerator:增加WorkerID自动分配功能 - 美团Leaf:结合号段模式优化高并发场景
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值