概述


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

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

  1. 一秒可生成500万ID的分布式自增ID算法—雪花算法 (Snowflake,Delphi 版)

    概述 分布式系统中,有一些需要使用全局唯一ID的场景,这种时候为了防止ID冲突可以使用36位的UUID,但是UUID有一些缺点,首先他相对比较长,另外UUID一般是无序的. 有些时候我们希望能使用一种 ...

  2. Twitter分布式自增ID算法snowflake原理解析(Long类型)

    Twitter分布式自增ID算法snowflake,生成的是Long类型的id,一个Long类型占8个字节,每个字节占8比特,也就是说一个Long类型占64个比特(0和1). 那么一个Long类型的6 ...

  3. 分布式自增ID算法-Snowflake详解

    1.Snowflake简介 互联网快速发展的今天,分布式应用系统已经见怪不怪,在分布式系统中,我们需要各种各样的ID,既然是ID那么必然是要保证全局唯一,除此之外,不同当业务还需要不同的特性,比如像并 ...

  4. Twitter分布式自增ID算法snowflake原理解析

    以JAVA为例 Twitter分布式自增ID算法snowflake,生成的是Long类型的id,一个Long类型占8个字节,每个字节占8比特,也就是说一个Long类型占64个比特(0和1). 那么一个 ...

  5. 详解Twitter开源分布式自增ID算法snowflake(附演算验证过程)

    详解Twitter开源分布式自增ID算法snowflake,附演算验证过程 2017年01月22日 14:44:40 url: http://blog.csdn.net/li396864285/art ...

  6. 【Java】分布式自增ID算法---雪花算法 (snowflake,Java版)

    一般情况,实现全局唯一ID,有三种方案,分别是通过中间件方式.UUID.雪花算法. 方案一,通过中间件方式,可以是把数据库或者redis缓存作为媒介,从中间件获取ID.这种呢,优点是可以体现全局的递增 ...

  7. 说起分布式自增ID只知道UUID?SnowFlake(雪花)算法了解一下(Python3.0实现)

    原文转载自「刘悦的技术博客」https://v3u.cn/a_id_155 但凡说起分布式系统,我们肯定会对一些海量级的业务进行分拆,比如:用户表,订单表.因为数据量巨大一张表完全无法支撑,就会对其进 ...

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

    概述 分布式系统中,有一些需要使用全局唯一ID的场景,这种时候为了防止ID冲突可以使用36位的UUID,但是UUID有一些缺点,首先他相对比较长,另外UUID一般是无序的. 有些时候我们希望能使用一种 ...

  9. 分布式自增ID算法snowflake (Java版)

    概述 分布式系统中,有一些需要使用全局唯一ID的场景,这种时候为了防止ID冲突可以使用36位的UUID,但是UUID有一些缺点,首先他相对比较长,另外UUID一般是无序的. 有些时候我们希望能使用一种 ...

随机推荐

  1. kindeditor编辑器微软雅黑样式font-family值变成&quot;

    http://www.100cm.cn/article-126-764.html kindeditor编辑器中选中文字, 修改字体(字体名称中带有空格, 例如"Microsoft YaHei ...

  2. Postman接口测试工具学习笔记

    - 新建测试接口 在Postman中有两种新建测试接口的方式,第一种是图片右上角的,点击可以选择 request 请求进行新建 选择 Request 以后会出现下面图片的对话框,让你输入一个保存接口的 ...

  3. ElasticSearch从不懂到会用1—安装篇

    连续加班近一个多月,项目终于告一段落了,也腾出时间写一写项目中用到的东西.在这个项目中,我负责的主要是多种业务场景下的数据查询和搜索,其中搜索用到了ElasticSearch搜索引擎.下面主要围绕El ...

  4. TCP和UDP的联系和用途

    一.区别        二者都是有用的和常用的,如果纯粹从概念上区分二者就比较费解了,我们直接从功能上进行区分,简单明了:        这两种传输协议也就是合于适配不同的业务和不同的硬件终端.    ...

  5. 11-28\enum

    1.创建一个枚举对象,对象中4个属性video视频.book书----(这2个属性可以用数字表示). 2.创建一个class对象,对象中有2个属性,一个是id属性(自己设置),第二个属性是type类型 ...

  6. H3CSTP、RSTP的问题

  7. redux.js的基本使用

    1.先是安装reduxJx, cnpm i --save rudux 2.创建一个store的js文件 3.使用import来引用 redux import { createStore } from ...

  8. C# 线程参数

    . class ThreadSample { private readonly int _iterations; public ThreadSample(int iterations) { _iter ...

  9. 【35.12%】【POJ 1988】Cube Stacking

    Time Limit: 2000MS Memory Limit: 30000K Total Submissions: 24007 Accepted: 8432 Case Time Limit: 100 ...

  10. dotnet 如何在 Mock 模拟 Func 判断调用次数

    在 dotnet 程序有很好用的 Mock 框架,可以用来模拟各种接口和抽象类,可以用来测试某个注入接口的被调用次数和被调用时传入参数.本文告诉大家如何在 Mock 里面模拟一个 Func 同时模拟返 ...