long 雪花算法_分布式id生成利器雪花算法

介绍

雪花算法是 twitter 开源的由 64 位整数组成的分布式id。目的是在分布式系统中产生全局唯一且趋势递增的ID

其核心思想就是:使用一个 64 bitlong 型的数字作为全局唯一 id。在分布式系统中的应用十分广泛,且ID 引入了时间戳,保持自增性且不重复。

雪花算法的结构

51da1c62e10ed661e18b7dde04be53a2.png

  • 标识:没有实际意义。一般都是0,都是正数。
  • 时间戳:41 bit 可以表示的数字多达 2^41 - 1,也就是可以标识 2 ^ 41 - 1 个毫秒值,换算成年就是表示 69 年的时间。
  • 机器id: 这里标识的是机器的唯一标识,一般由两部分构成:机房id+机器id。一共10位,可以表示1024台机器。
  • 序列号:可以用这个 12 bit 代表的数字来区分同一个毫秒内的 4096 个不同的 id。也就是同一毫秒内同一台机器所生成的最大ID数量为4096

雪花算法的工作流程

以一个简单的雪花算法工作流程来说。假设有一个服务假设要生成一个全局唯一id,那么就可以发送一个请求给部署了 SnowFlake 算法的系统,由这个 SnowFlake 算法系统来生成唯一 id。这个 SnowFlake 算法系统首先肯定是知道自己所在的机器号,(假设机器id为10bit)接着 SnowFlake 算法系统接收到这个请求之后,首先就会用二进制位运算的方式生成一个 64 bitlongid64bit 中的第一个 bit 是无意义的。接着用当前时间戳(单位到毫秒)占用41bit,然后接着 10bit 设置机器 id。最后再判断一下,当前这台机房的这台机器上这一毫秒内,这是第几个请求,给这次生成 id 的请求累加一个序号,作为最后的 12bit

雪花算法实现

package com.fxb.algorithm;

/**
 * 雪花算法生成器
 * 


 * 1.todo: 可以指定不同数据位数。
 * 2.todo: 单例
 *
 * @author fangjiaxiaobai
 * @date 2020-10-24 23:51
 * @since 1.0
 */


public class SnowFlakeGenerator {

    /**
     * 开始时间戳 (2015-01-01)
     */
    private final static long DEFAULT_TIMESTAMP = 1603556068000L;

    /**
     * 总位数
     */
    private final static long BITS_COUNT = 64L;
    /**
     * 机器id所占的位数,
     * 默认是5位。最多支持31台机器
     */
    private final static long DEFAULT_WORKER_ID_BITS = 5L;

    /**
     * 数据中心id所占的位数
     * 可以理解为机房。默认是5位。
     */
    private final static long DEFAULT_DATA_CENTER_ID_BITS = 5L;

    /**
     * 序列在id中占的位数
     */
    private final static long DEFAULT_SEQUENCE_BITS = 12L;

    /**
     * 支持的最大机器id,结果是31 (这个移位算法可以很快的计算出几位二进制数所能表示的最大十进制数)
     */
    private final static long DEFAULT_MAX_WORKER_ID = ~(-1L <
    /**
     * 支持的数据中心数量
     */
    private final static long DEFAULT_MAX_DATA_CENTER_ID_BITS = ~(-1L <
    /**
     * 生成序列的掩码,这里为4095 (0b111111111111=0xfff=4095)
     */
    private final static long DEFAULT_SEQUENCE_MASK = ~(-1L <
    /**
     * 时间戳向左移22位(5+5+12)
     */
    private final static long DEFAULT_TIMESTAMP_LEFT_SHIFT = DEFAULT_SEQUENCE_BITS + DEFAULT_WORKER_ID_BITS + DEFAULT_DATA_CENTER_ID_BITS;

    /**
     * 数据标识id向左移17位(12+5)
     */
    private final static long DEFAULT_DATA_CENTER_ID_SHIFT = DEFAULT_SEQUENCE_BITS + DEFAULT_WORKER_ID_BITS;

    /**
     * workId的位移
     */
    private final static long DEFAULT_WORKER_ID_SHIFT = DEFAULT_SEQUENCE_BITS;

    /**
     * 工作机器ID
     */
    private long workerId;

    /**
     * 数据中心ID
     */
    private long dataCenterId;

    /**
     * 支持的最大数据中心数
     */
    private long maxDataCenterId;

    /**
     * 支持的最大workerid数
     */
    private long maxWorkerId;

    /**
     * 序列号的位数
     */
    private Long sequenceBits;

    /**
     * 序列号的掩码
     * 即,2^sequenceBits
     */
    private Long sequenceMask;

    /**
     * 毫秒内序列
     */
    private long sequence = 0L;

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

    private long dataCenterIdShift;


    private long workerIdShift;
    /**
     * 时间戳的位数
     */
    private long timestampLeftShift;

    /**
     * 默认 每个数据中心可以容纳31台机器
     *
     * @param workerId     工作ID
     * @param dataCenterId 数据中心ID
     */
    public SnowFlakeGenerator(long workerId, long dataCenterId) {
        //...
    }

    /**
     * 根据自己的配置决定容纳的机器数
     *
     * @param workerId         工作机器的id
     * @param dataCenterId     数据中心的id
     * @param workerIdBits     工作机占的位数
     * @param dataCenterIdBits 数据中心占的位数
     */
    public SnowFlakeGenerator(long workerId, long dataCenterId, long workerIdBits, long dataCenterIdBits) {
      // ...
    }

    /**
     * 获得下一个ID (该方法是线程安全的)
     *
     * @return SnowflakeId
     */
    public synchronized long nextId() {
        long currentTimestamp = getCurrentTimeStamp();

        //如果当前时间小于上一次ID生成的时间戳,说明系统时钟回退过这个时候应当抛出异常
        if(currentTimestamp             throw new RuntimeException(
                    String.format("Clock moved backwards.  Refusing to generate id for %d milliseconds", lastTimestamp - currentTimestamp));
        }

        //如果是同一时间生成的,则进行毫秒内序列
        if(lastTimestamp == currentTimestamp) {
            sequence = (sequence + 1) & sequenceMask;
            //毫秒内序列溢出
            if(sequence == 0) {
                //阻塞到下一个毫秒,获得新的时间戳
                currentTimestamp = tilNextMillis(lastTimestamp);
            }
        }
        //时间戳改变,毫秒内序列重置
        else {
            sequence = 0L;
        }

        //上次生成ID的时间戳
        lastTimestamp = currentTimestamp;

        //移位并通过或运算拼到一起组成64位的ID
        return ((currentTimestamp - DEFAULT_TIMESTAMP) <                | (dataCenterId <                | (workerId <                | sequence;
    }

    /**
     * 阻塞到下一个毫秒,直到获得新的时间戳
     *
     * @param lastTimestamp 上次生成ID的时间戳
     * @return 当前时间戳
     */
    protected long tilNextMillis(long lastTimestamp) {
        long timestamp = getCurrentTimeStamp();
        while (timestamp <= lastTimestamp) {
            timestamp = getCurrentTimeStamp();
        }
        return timestamp;
    }

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

完整代码: http://dwz.date/cWmR

公众号中回复 【雪花算法】,可以直接获取源码文件。

最后

我在【方家小白】等你,期望和你一起遇见更好的自己!

ad22bf24b2876f165fad0f3770eb3c91.png

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值