雪花算法snowflake原理及实现

Twitter(推特)的SnowFlake算法是一种著名的分布式服务器用户ID生成算法。

数据结构

SnowFlake算法所生成的ID是一个64bit的长整型数字,如图所示。这个64bit被划分成四个部分,其中后面三个部分分别表示时间戳、工作机器ID、序列号。

image.png

SnowFlakeID的四个部分,具体介绍如下:

第一位:占用1 bit,其值始终是0,没有实际作用。
时间戳:占用41 bit,精确到毫秒,总共可以容纳约69年的时间。
工作机器id:占用10 bit,最多可以容纳1024个节点。
序列号:占用12 bit,这个值在同一毫秒同一节点上从0开始不断累加,最多可以累加到4095。

在工作节点达到1024顶配的场景下,SnowFlake算法在同一毫秒最多可以生成的ID数量为:1024 * 4096 =4194304(百万级),在绝大多数并发场景下都是够用的。

优缺点

SnowFlake算法的优点:

  1. 生成ID时不依赖于数据库,完全在内存生成,高性能和高可用性。
  2. 容量大,每秒可生成几百万个ID。
  3. ID呈趋势递增,后续插入数据库的索引树时,性能较高。

SnowFlake算法的缺点:

  1. 依赖于系统时钟的一致性,如果某台机器的系统时钟回拨了,有可能造成ID冲突,或者ID乱序。
  2. 在启动之前,如果这台机器的系统时间回拨过,那么有可能出现ID重复的危险。

雪花算法实现

基于zookeeper实现

public class SnowflakeIdGenerator {

    /**
     * 单例
     */
    public static SnowflakeIdGenerator instance =
            new SnowflakeIdGenerator();


    /**
     * 初始化单例
     *
     * @param workerId 节点Id,最大8091
     * @return the 单例
     */
    public synchronized void init(long workerId) {
        if (workerId > MAX_WORKER_ID) {
            // zk分配的workerId过大
            throw new IllegalArgumentException("woker Id wrong: " + workerId);
        }
        instance.workerId = workerId;
    }

    private SnowflakeIdGenerator() {

    }


    /**
     * 开始使用该算法的时间为: 2017-01-01 00:00:00
     */
    private static final long START_TIME = 1483200000000L;

    /**
     * worker id 的bit数,最多支持8192个节点
     */
    private static final int WORKER_ID_BITS = 13;

    /**
     * 序列号,支持单节点最高每毫秒的最大ID数1024
     */
    private final static int SEQUENCE_BITS = 10;

    /**
     * 最大的 worker id ,8091
     * -1 的补码(二进制全1)右移13位, 然后取反
     */
    private final static long MAX_WORKER_ID = ~(-1L << WORKER_ID_BITS);

    /**
     * 最大的序列号,1023
     * -1 的补码(二进制全1)右移10位, 然后取反
     */
    private final static long MAX_SEQUENCE = ~(-1L << SEQUENCE_BITS);

    /**
     * worker 节点编号的移位
     */
    private final static long WORKER_ID_SHIFT = SEQUENCE_BITS;

    /**
     * 时间戳的移位
     */
    private final static long TIMESTAMP_LEFT_SHIFT = WORKER_ID_BITS + SEQUENCE_BITS;

    /**
     * 该项目的worker 节点 id
     */
    private long workerId;

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

    /**
     * 当前毫秒生成的序列
     */
    private long sequence = 0L;

    /**
     * Next id long.
     *
     * @return the nextId
     */
    public Long nextId() {
        return generateId();
    }

    /**
     * 生成唯一id的具体实现
     */
    private synchronized long generateId() {
        long current = System.currentTimeMillis();

        if (current < lastTimestamp) {
            // 如果当前时间小于上一次ID生成的时间戳,说明系统时钟回退过,出现问题返回-1
            return -1;
        }

        if (current == lastTimestamp) {
            // 如果当前生成id的时间还是上次的时间,那么对sequence序列号进行+1
            sequence = (sequence + 1) & MAX_SEQUENCE;

            if (sequence == MAX_SEQUENCE) {
                // 当前毫秒生成的序列数已经大于最大值,那么阻塞到下一个毫秒再获取新的时间戳
                current = this.nextMs(lastTimestamp);
            }
        } else {
            // 当前的时间戳已经是下一个毫秒
            sequence = 0L;
        }

        // 更新上次生成id的时间戳
        lastTimestamp = current;

        // 进行移位操作生成int64的唯一ID

        //时间戳右移动23位
        long time = (current - START_TIME) << TIMESTAMP_LEFT_SHIFT;

        //workerId 右移动10位
        long workerId = this.workerId << WORKER_ID_SHIFT;

        return time | workerId | sequence;
    }

    /**
     * 阻塞到下一个毫秒
     */
    private long nextMs(long timeStamp) {
        long current = System.currentTimeMillis();
        while (current <= timeStamp) {
            current = System.currentTimeMillis();
        }
        return current;
    }
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论
### 回答1: Java雪花算法Snowflake Algorithm)是一种分布式系统中用来生成唯一的ID算法。它由Twitter的工程师李清松在2010年提出。 雪花算法原理是:使用一个64位的long类型的整数来存储ID,其中: - 1位标识位(最高位):用于标识ID是正数还是负数。因为long类型的整数默认是正数,所以这一位可以忽略。 - 41位时间戳位(第2~42位):用于记录ID生成的时间戳。这41位可以表示$2^{41}-1$个单位时间,也就是说可以使用$2^{41}-1$年。 - 10位节点位(第43~52位):用于记录ID生成机器所在的节点。 - 12位序列号位(第53~64位):用于记录在同一时间戳内生成的不同ID。 下面是Java代码的实现示例: ``` public class SnowflakeIdWorker { // 开始时间戳(2020-01-01) private final long startTimestamp = 1577836800000L; // 机器ID所占的位数 private final long workerIdBits = 5L; // 数据中心ID所占的位数 private final long dataCenterIdBits = 5L; // 序列号所占的位数 private final long sequenceBits = 12L; // 机器ID的最大值 private final long maxWorkerId = -1L ^ (-1L << workerIdBits); // 数据中心ID的最大值 private final long maxDataCenterId = -1L ^ (-1L << dataCenterIdBits); // 序列号的最大值 private final long max ### 回答2: 雪花算法是一种分布式唯一ID生成算法,常用于生成全局唯一的ID。下面是Java中实现雪花算法的简要步骤: 1. 定义一个Snowflake类,其中包含以下属性: - startTime:系统的起始时间戳,用于计算生成的ID中的时间戳部分。 - datacenterId:数据中心的标识,可以根据实际需求进行调整。 - workerId:工作节点的标识,可以根据实际需求进行调整。 - sequence:序列号,用于处理同一毫秒内的并发请求。 2. 在Snowflake类中定义一个generateId()方法,用于生成唯一ID。 - 获取当前时间戳,并减去起始时间戳,得到时间戳部分。 - 根据实际情况,将时间戳、数据中心ID和工作节点ID按位进行拼接,生成ID的高位部分。 - 如果当前时间戳相同,则递增序列号部分;否则,将序列号重置为0。 - 将ID的高位部分与序列号进行拼接,生成最终的唯一ID。 3. 在主程序中调用generateId()方法,实例化Snowflake类并生成一定数量的唯一ID。 需要注意的是,雪花算法实现需要根据实际业务场景进行调整,比如数据中心ID和工作节点ID的位数、起始时间戳的设定等都需要根据实际需求进行适配。另外,为避免时钟回拨等问题,可以使用系统时间的补偿机制来处理。 ### 回答3: 雪花算法是一种生成全局唯一ID算法,主要用于分布式系统中,保证每个生成的ID在整个系统中都是唯一的。下面是Java实现雪花算法的代码示例: ```java public class SnowflakeIdGenerator { private final long startTime = 1609459200000L; // 设置起始时间戳,例如:2021-01-01 00:00:00 private final long workerIdBits = 5L; // 机器ID所占的位数 private final long maxWorkerId = -1L ^ (-1L << workerIdBits); // 最大机器ID private final long dataCenterIdBits = 5L; // 数据中心ID所占的位数 private final long maxDataCenterId = -1L ^ (-1L << dataCenterIdBits); // 最大数据中心ID private final long sequenceBits = 12L; // 序列号所占的位数 private final long workerIdShift = sequenceBits; // 机器ID向左移的位数 private final long dataCenterIdShift = sequenceBits + workerIdBits; // 数据中心ID向左移的位数 private final long timestampShift = sequenceBits + workerIdBits + dataCenterIdBits; // 时间戳向左移的位数 private final long sequenceMask = -1L ^ (-1L << sequenceBits); // 序列号的最大值 private long lastTimestamp = -1L; private long sequence = 0L; private final long workerId; private final long dataCenterId; public SnowflakeIdGenerator(long workerId, long dataCenterId) { if (workerId > maxWorkerId || workerId < 0) { throw new IllegalArgumentException("Worker ID超过范围"); } if (dataCenterId > maxDataCenterId || dataCenterId < 0) { throw new IllegalArgumentException("DataCenter ID超过范围"); } this.workerId = workerId; this.dataCenterId = dataCenterId; } public synchronized long nextId() { long timestamp = System.currentTimeMillis(); if (timestamp < lastTimestamp) { throw new RuntimeException("时钟回拨异常"); } if (timestamp == lastTimestamp) { sequence = (sequence + 1) & sequenceMask; if (sequence == 0) { timestamp = tilNextMillis(lastTimestamp); } } else { sequence = 0L; } lastTimestamp = timestamp; return ((timestamp - startTime) << timestampShift) | (dataCenterId << dataCenterIdShift) | (workerId << workerIdShift) | sequence; } private long tilNextMillis(long lastTimestamp) { long timestamp = System.currentTimeMillis(); while (timestamp <= lastTimestamp) { timestamp = System.currentTimeMillis(); } return timestamp; } } ``` 以上是一个基于Snowflake算法实现分布式ID生成器,通过构造函数传入机器ID和数据中心ID,然后调用`nextId()`方法就能够生成唯一的IDID的格式为一个64位的长整型数。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

Firechou

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值