欢迎您访问程序员文章站本站旨在为大家提供分享程序员计算机编程知识!
您现在的位置是: 首页

雪花算法(SnowFlake)-java

程序员文章站 2022-05-03 14:48:51
...

雪花算法

简介: 雪花算法 是 Twitter 开源的分布式id生成算法。使用一个64bit 的long型数字作为全局id。
优点:ID 自增,不会产生重复ID,在本地生成不会消耗网络效率高,存入数据库索引效率高。
缺点:依赖于系统时间的强一致性,如果系统时间回拨,或者改变,可能会造成生成重复id。

结构图如下:
雪花算法(SnowFlake)-java

  1. 1bit :该bit 不使用
    因为二进制中,最高位是符号位,1标识负数,0 标识 正数,生成的id 都是使用 正数标识,所以 该位 不使用,固定为 0.

  2. 41bit - 时间戳:
    41bit - 时间戳 = (当前时间戳(nowTimeMillis ) - 开始时间戳(beginTimeMillis))
    开始时间戳 :初始化的时候 是固定的,必须必nowTimeMillis 小。
    41bit 位 最多可表示 (2^41 - 1 )个数字,
    也就是 时间差为: (2^41 - 1 )个毫秒值,大概 69年多。

  3. 10bit : 包含 5 bit 数据中心 位 和 5 bit 机器位。
    即:最多可部署 2^5 (32)个 机房,
    每个机房 可以代表 2^5 (32)台机器。

  4. 12 bit:这个是用来记录同一个毫秒内产生的不同 id。
    即:同一毫秒 内 最多可以生产 2^12 -1 = 4095 个不同id,超过该值,必须要等倒下一毫秒才能继续生产id。

  5. 最后,将 以上 四部分,通过位移 加上 或 运算 组拼成一个 64 bit 位 的数值 即可。

  6. 核心 算法
    雪花算法(SnowFlake)-java

具体实现 如下

package com.nn.tools;

/**
 * @ClassName Snow
 * @Author nn
 * @Date 2020/7/24 10:33
 */
public class MySnowFlake {

    /**
     * 开始时间
     */
    private long beginTimeMillis = 1585644268888L;

    /**
     * 上一次 生产id 的时间
     */
    private long lastTimeMillis = -1L;

    /**
     * 当前时间
     */
    private long nowTimeMillis;

    /**
     * 机房id
     */
    private long machineRoomId;

    /**
     * 机房 id 所占 位数(总 64位)
     */
    private long machineRoomIdBits = 5L;

    /**
     * 最大 支持机房id值 即:2^5 -1 = 31  (0-31)
     */
    private long maxMachineRoomId = -1L ^ (-1L << machineRoomIdBits);

    /**
     * 机器id
     */
    private long machineId;


    /**
     * 机器id 所占 位数(总 64位)
     */
    private long machineIdBits = 5L;


    /**
     * 最大 支持机器id值 即:2^5 -1 = 31  (0-31)
     */
    private long maxMachineId = -1L ^ (-1L << machineRoomIdBits);


    /**
     * 一毫秒内 最多允许生成id 个数 所占位数
     */
    private long sequenceBits = 12L;

    /**
     * 一毫秒内 最多允许生成id 个数
     * (2^12 - 1) = 4095 , 一毫秒内 最多允许生产 4095 个id
     * 超过该值时 进入等待,下一毫秒才可以继续生产
     * -1L ---> 2进制为:1...(62位)...1   64位都为1
     * -1L ^ (-1L << sequenceBits) = (2^12 - 1) = 4095
     */
    private long MaxSequenceNumInOneMs = -1L ^ (-1L << sequenceBits);


    /**
     * 代表一毫秒内生成的最新序号
     */
    private long sequenceNum;


    /**
     * 时间戳部分 需要左移 的位数;42
     */
    private long timeLeftBits = machineRoomIdBits + machineIdBits + sequenceBits;

    /**
     * 机房部分 需要左移 的位数;17
     */
    private long machineRoomLeftBits = machineIdBits + sequenceBits;

    /**
     * 机器部分 需要左移 的位数;12
     */
    private long machineLeftBits = sequenceBits;


    public MySnowFlake(long machineRoomId, long machineId) {
        /**
         *  判断 输入的 machineRoomId 是否在[0-31]范围内
         */
        if (machineRoomId > maxMachineRoomId || machineRoomId < 0) {
            throw new IllegalArgumentException(
                    String.format("worker Id can't be greater than %d or less than 0", machineRoomId));
        }

        /**
         *  判断 输入的 machineId 是否在[0-31]范围内
         */
        if (machineId > maxMachineId || machineId < 0) {

            throw new IllegalArgumentException(
                    String.format("datacenter Id can't be greater than %d or less than 0", machineId));
        }

        this.machineRoomId = machineRoomId;
        this.machineId = machineId;

    }

    public synchronized long produceNextId() {

        /**
         *  获取 当前 时间戳
         */
        nowTimeMillis = getNowTimeStamp();

        if (lastTimeMillis == nowTimeMillis) {

            /**
             * MaxSequenceNumInOneMs:sequenceNum 的 最大值 4095(2^12 - 1)其二进制为:0000 1111 1111 1111
             *  当 sequenceNum = 4095 时(0000 1111 1111 1111) 加 1  得到-->2^12 (0001 0000 0000 0000)
             *  0001 0000 0000 0000 & 0000 1111 1111 1111  = 0
             *
             *  即,当 sequenceNum = 0 的时候 就不能继续生产id,需要等待下一毫秒才可以继续生产id,即进入等待。
             */
            sequenceNum = (sequenceNum + 1) & MaxSequenceNumInOneMs;

            /**
             * 当某一毫秒的时间,产生的id数 超过4095,系统会进入等待,直到下一毫秒,系统继续产生ID
             */
            if (sequenceNum == 0) {
                nowTimeMillis = getNowTimeStamp();
                while (nowTimeMillis == lastTimeMillis) {
                    nowTimeMillis = getNowTimeStamp();
                }
            }
        } else {
            sequenceNum = 0;
        }
        lastTimeMillis = nowTimeMillis;

        /**
         *  id = 时间戳部分(41位) | 机房id(5位) | 机器id(5位) | 1 毫秒内 id***(12位) = 64 二进制位 值
         */
        long a = ((nowTimeMillis - beginTimeMillis) << timeLeftBits) |
                (machineRoomId << machineRoomLeftBits) |
                (machineId << machineLeftBits) |
                sequenceNum;

        return a;

    }


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


    public static void main(String[] args) {
        MySnowFlake snow = new MySnowFlake(1, 1);
        for (int i = 0; i < 10000; i++) {
            long l = snow.produceNextId();
            System.out.println(l);
        }
    }
}