雪花算法(SnowFlake)-java

雪花算法

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

结构图如下:
雪花算法(SnowFlake)-java_第1张图片

  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_第2张图片

具体实现 如下

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);
        }
    }
}

你可能感兴趣的:(算法)