概述


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. Wunder Fund Round 2016 (Div. 1 + Div. 2 combined)

    现在水平真的不够.只能够做做水题 A. Slime Combining 题意:就是给n个1给你.两个相同的数可以合并成一个数,比如说有两个相同的v,合并后的值就是v+1 思路:直接模拟栈 #inclu ...

  2. Pipeline & PageProcesser

    Pipeline & PageProcesser 这两部分是应该程序员自己实现的部分,因为PageProcesser关乎如何解析页面而Pipeline则是存储,推荐使用OOSpider也就是注 ...

  3. Xshell + SVN使用

    切换目录 cd+想跳转到的目录下 文件浏览 ls ll (ll 信息全) svn更新 svn up 编辑 vi vi的命令 文件保存与退出: :q 在文件未作任何修改的情况下退出. :q! 强制退出, ...

  4. java 内存操作流

    操作内存流的时候(从读取出来,注意一定要把真正的数据用toByteArray或者toCharArray将数据读出来) 之前的文件操作流是以文件的输入输出为主的,当输出的位置变成了内存,那么就称为内存操 ...

  5. PHP mysql扩展整理,操作数据库的实现过程分析

    相关文章:PHP mysqli扩展整理,包括面向过程和面向对象的比较\事务控制\批量执行\预处理   PHPmysqli扩展整理,包括面向过程和面向对象的比较\事务控制\批量执行\预处理 从某种程度上 ...

  6. tf.concat()

    转载自:https://blog.csdn.net/appleml/article/details/71023039 https://www.cnblogs.com/mdumpling/p/80534 ...

  7. Spring Security 学习笔记-securityContext过滤器

    位于过滤器顶端,第一个起作用的过滤器.SecurityContextPersistenceFilter 在执行其他过滤器之前,率先判断用户的session中是否已经存在一个SecurityContex ...

  8. 关于redux和react-redux使用combinereducers之后的问题

    最近用react写项目的时候,开始复习之前学过的redux,记录一下一些坑,以防忘记 我现在的redux目录下有这么些东西 首先是index.js import { createStore } fro ...

  9. Mybatis与Spring集成(易百教程)

    整个Mybatis与Spring集成示例要完成的步骤如下: 1.示例功能描述 2.创建工程 3.数据库表结构及数据记录 4.实例对象 5.配置文件 6.测试执行,输出结果 1.示例功能描述 在本示例中 ...

  10. xcode无线调试

    前言: xcode9 以上才会有无线调试这个功能,换了一个type-c口的mac,公司的新电脑,但是公司不给配转接口,到某东看了一下,type-c口同时可以转化usb和VGA的要198,官网差不多50 ...