SnowflakeId雪花ID算法,分布式自增ID应用

摘自:https://www.cnblogs.com/zhou-920644981/p/12202391.html

概述



snowflake是Twitter开源的分布式ID生成算法,结果是一个Long型的ID。其核心思想是:使用41bit作为毫秒数,10bit作为机器的ID(5个bit是数据中心,5个bit的机器ID),12bit作为毫秒内的序列号(意味着每个节点在每毫秒可以产生 4096 个 ID),最后还有一个符号位,永远是0。

特点:

  1. 作为ID,肯定是唯一的;
  2. 自增,依赖时间戳生成,序列号有序递增;
  3. 支持非常大的业务ID生成,最大支持2^10=1024个业务节点,同一个节点一毫秒最多生成2^12=4096个ID,41位毫秒级时间可以使用(2^41 - 1)/(1000*60*60*24*365)=69.73,大约70年;
  4. 实现简单,不依赖于其他第三方组件,甚至都不需要任何import。

结果是一个Long型的ID,64位,结构图如下:

  1. 第1位固定是0,表示正数;
  2. 第2-42共41位表示时间戳,当前时间的时间戳减去开始时间的时间戳;
  3. 业务节点ID,每个节点固定的值;
  4. 毫秒内的序列号。

实现



清楚了结构后,就比较好实现了。

41bit-时间戳

当前时间的时间戳减去开始时间的时间戳,左移22位

(ts - beginTs) << timestampLeftOffset

10bit-工作机器ID

自定义的业务节点ID,固定的值,左移12位

workerId << workerIdLeftOffset

12bit-序列号

毫秒内序列号,以此递增,如果溢出就阻塞到下一秒从0开始计数

// 同一时间内,则计算序列号
if (ts == lastTimestamp) {
    // 序列号溢出
    if (++sequence > maxSequence) {
        ts = tilNextMillis(lastTimestamp);
        sequence = 0L;
    }
} else {
    // 时间戳改变,重置序列号
    sequence = 0L;
}

lastTimestamp = ts;

/**
 * 阻塞到下一个毫秒
 *
 * @param lastTimestamp
 * @return
 */
private long tilNextMillis(long lastTimestamp) {
    long ts = System.currentTimeMillis();
    while (ts <= lastTimestamp) {
        ts = System.currentTimeMillis();
    }

    return ts;
}

生成最终的ID

return (ts - beginTs) << timestampLeftOffset | workerId << workerIdLeftOffset | sequence;

完整代码



最后贴出完整代码。

public class SnowflakeIdWorker {

    /**
     * 开始时间:2020-01-01 00:00:00
     */
    private final long beginTs = 1577808000000L;

    private final long workerIdBits = 10;

    /**
     * 2^10 - 1 = 1023
     */
    private final long maxWorkerId = -1L ^ (-1L << workerIdBits);

    private final long sequenceBits = 12;

    /**
     * 2^12 - 1 = 4095
     */
    private final long maxSequence = -1L ^ (-1L << sequenceBits);

    /**
     * 时间戳左移22位
     */
    private final long timestampLeftOffset = workerIdBits + sequenceBits;

    /**
     * 业务ID左移12位
     */
    private final long workerIdLeftOffset = sequenceBits;

    /**
     * 合并了机器ID和数据标示ID,统称业务ID,10位
     */
    private long workerId;

    /**
     * 毫秒内序列,12位,2^12 = 4096个数字
     */
    private long sequence = 0L;

    /**
     * 上一次生成的ID的时间戳,同一个worker中
     */
    private long lastTimestamp = -1L;

    public SnowflakeIdWorker(long workerId) {
        if (workerId > maxWorkerId || workerId < 0) {
            throw new IllegalArgumentException(String.format("WorkerId必须大于或等于0且小于或等于%d", maxWorkerId));
        }

        this.workerId = workerId;
    }

    public synchronized long nextId() {
        long ts = System.currentTimeMillis();
        if (ts < lastTimestamp) {
            throw new RuntimeException(String.format("系统时钟回退了%d毫秒", (lastTimestamp - ts)));
        }

        // 同一时间内,则计算序列号
        if (ts == lastTimestamp) {
            // 序列号溢出
            if (++sequence > maxSequence) {
                ts = tilNextMillis(lastTimestamp);
                sequence = 0L;
            }
        } else {
            // 时间戳改变,重置序列号
            sequence = 0L;
        }

        lastTimestamp = ts;

        // 0 - 00000000 00000000 00000000 00000000 00000000 0 - 00000000 00 - 00000000 0000
        // 左移后,低位补0,进行按位或运算相当于二进制拼接
        // 本来高位还有个0<<63,0与任何数字按位或都是本身,所以写不写效果一样
        return (ts - beginTs) << timestampLeftOffset | workerId << workerIdLeftOffset | sequence;
    }

    /**
     * 阻塞到下一个毫秒
     *
     * @param lastTimestamp
     * @return
     */
    private long tilNextMillis(long lastTimestamp) {
        long ts = System.currentTimeMillis();
        while (ts <= lastTimestamp) {
            ts = System.currentTimeMillis();
        }

        return ts;
    }
}

补充



这里面有大量的二进制位运算,目的只有一个:快。

规则:1为真,0为否,其实就是同位之间的布尔运算。

按位与:&

都为真就是真,其他都是否

1&1=1
1&0=0
0&1=0
0&0=0

按位或:|

只要有一个真就是真

1|1=1
1|0=1
0|1=1
0|0=0

异或:^

相同就是否,不同就是真

1^1=0
1^0=1
0^1=1
0^0=0

左移:<<

所有位向左移动多少位,低位补0,高位多出的直接删掉

右移:>>

所有位向右移动多少位,低位多出的删掉,高位是0补0,是1就补1

Java中的原码、补码和反码

原码

原码就是十进制数字的原始二进制表示,对于整数而言,最高位为符号位,1表示负数,0表示正数。以32位int型的整数2及-2举例:

2的原码:00000000 00000000 00000000 00000010

-2的原码:10000000 00000000 00000000 00000010

反码

正数的反码就是其原码,负数的反码除了最高位的符号位外,其他位取反(0改1,1改0)

2的反码:00000000 00000000 00000000 00000010

-2的反码:11111111 11111111 11111111 11111101

补码

正数的补码就是其原码,负数的补码是其反码加1

2的补码:00000000 00000000 00000000 00000010

-2的反码:11111111 11111111 11111111 11111101

-2的补码:11111111 11111111 11111111 11111110

标签: snowflake

原文地址:https://www.cnblogs.com/xichji/p/12208435.html

时间: 2024-08-30 16:30:15

SnowflakeId雪花ID算法,分布式自增ID应用的相关文章

[转] Twitter的分布式自增ID算法Snowflake实现分析及其Java、Php和Python版

转载自:http://www.dengchuanhua.com/132.html 在分布式系统中,需要生成全局UID的场合还是比较多的,twitter的snowflake解决了这种需求,实现也还是很简单的,除去配置信息,核心代码就是毫秒级时间41位+机器ID 10位+毫秒内序列12位. 该项目地址为:https://github.com/twitter/snowflake是用Scala实现的. python版详见开源项目https://github.com/erans/pysnowflake.

Twitter的分布式自增ID算法snowflake (Java版)

概述 分布式系统中,有一些需要使用全局唯一ID的场景,这种时候为了防止ID冲突可以使用36位的UUID,但是UUID有一些缺点,首先他相对比较长,另外UUID一般是无序的. 有些时候我们希望能使用一种简单一些的ID,并且希望ID能够按照时间有序生成. 而twitter的snowflake解决了这种需求,最初Twitter把存储系统从MySQL迁移到Cassandra,因为Cassandra没有顺序ID生成机制,所以开发了这样一套全局唯一ID生成服务. 结构 snowflake的结构如下(每部分用

转:snowflake分布式自增ID算法

原文地址:http://www.cnblogs.com/relucent/p/4955340.html 概述 分布式系统中,有一些需要使用全局唯一ID的场景,这种时候为了防止ID冲突可以使用36位的UUID,但是UUID有一些缺点,首先他相对比较长,另外UUID一般是无序的. 有些时候我们希望能使用一种简单一些的ID,并且希望ID能够按照时间有序生成. 而twitter的snowflake解决了这种需求,最初Twitter把存储系统从MySQL迁移到Cassandra,因为Cassandra没有

分布式自增ID算法snowflake

分布式系统中,有一些需要使用全局唯一ID的场景,这种时候为了防止ID冲突可以使用36位的UUID,但是UUID有一些缺点,首先他相对比较长,另外UUID一般是无序的,作为索引非常不好,严重影响性能. snowflake的结构如下(每部分用-分开): 0 - 0000000000 0000000000 0000000000 0000000000 0 - 00000 - 00000 - 000000000000 第一个部分,是 1 个 bit:0,这个是无意义的. 第二个部分是 41 个 bit:表

snowflake 雪花算法 分布式实现全局id生成

snowflake是Twitter开源的分布式ID生成算法,结果是一个long型的ID. 这种方案大致来说是一种以划分命名空间(UUID也算,由于比较常见,所以单独分析)来生成ID的一种算法,这种方案把64-bit分别划分成多段,分开来标示机器.时间等. 其核心思想是:使用41bit作为毫秒数,10bit作为机器的ID(5个bit是数据中心,5个bit的机器ID),12bit作为毫秒内的流水号,最后还有一个符号位,永远是0. 比如在snowflake中的64-bit分别表示如下图(图片来自网络)

.NET 分布式自增Id组件(解决自动分配机器Id、时间回拨问题)

目录 简介 产生背景 使用方式 原始版 完美版 测试 结尾 简介 IdHelper是一个.NET(支持.NET45+或.NET Standard2+)生成分布式趋势自增Id组件,有两个版本:原始版为基于雪花Id(不了解请自行百度)方案,需要手动管理设置WorkerId:完美版在原始版的基础上使用Zookeeper来解决原始版中的WorkerId的分配问题和时间回拨问题. 原始版安装方式:Nuget安装IdHelper即可 完美版安装方式:Nuget安装IdHelper.Zookeeper即可 请

Twitter的分布式自增ID算法snowflake(雪花算法) - C#版

概述 分布式系统中,有一些需要使用全局唯一ID的场景,这种时候为了防止ID冲突可以使用36位的UUID,但是UUID有一些缺点,首先他相对比较长,另外UUID一般是无序的.有些时候我们希望能使用一种简单一些的ID,并且希望ID能够按照时间有序生成.而twitter的snowflake解决了这种需求,最初Twitter把存储系统从MySQL迁移到Cassandra,因为Cassandra没有顺序ID生成机制,所以开发了这样一套全局唯一ID生成服务. 该项目地址为:https://github.co

C# 分布式自增ID算法snowflake(雪花算法)

概述 分布式系统中,有一些需要使用全局唯一ID的场景,这种时候为了防止ID冲突可以使用36位的UUID,但是UUID有一些缺点,首先他相对比较长,另外UUID一般是无序的.有些时候我们希望能使用一种简单一些的ID,并且希望ID能够按照时间有序生成.而twitter的snowflake解决了这种需求,最初Twitter把存储系统从MySQL迁移到Cassandra,因为Cassandra没有顺序ID生成机制,所以开发了这样一套全局唯一ID生成服务. 该项目地址为:https://github.co

Java中SnowFlake 雪花算法生成全局唯一id中的问题,时间不连续全为偶数解决

package com.example.springbootshardingjdbc.util; import java.io.FileOutputStream; /** * 描述: Twitter的分布式自增ID雪花算法snowflake (Java版) * * @author * @create 2018-03-13 12:37 **/ public class SnowFlake { /** * 起始的时间戳 */ private final static long START_STMP