分布式系统中,有一些需要使用全局唯一ID的场景,这种时候为了防止ID冲突可以使用36位的UUID,但是UUID有一些缺点,首先他相对比较长,另外UUID一般是无序的,作为索引非常不好,严重影响性能。

snowflake的结构如下(每部分用-分开):

0 - 0000000000 0000000000 0000000000 0000000000 0 - 00000 - 00000 - 000000000000

  • 第一个部分,是 1 个 bit:0,这个是无意义的。

  • 第二个部分是 41 个 bit:表示的是时间戳。

  • 第三个部分是 5 个 bit:表示的是机房 id,10001。

  • 第四个部分是 5 个 bit:表示的是机器 id,1 1001。

  • 第五个部分是 12 个 bit:表示的序号,就是某个机房某台机器上这一毫秒内同时生成的 id 的序号,0000 00000000。

snowflake生成的ID整体上按照时间自增排序,并且整个分布式系统内不会产生ID碰撞(由datacenter和workerId作区分),并且效率较高。经测试snowflake每秒能够产生26万个ID。

  1. /**
  2. * Twitter_Snowflake<br>
  3. * SnowFlake的结构如下(每部分用-分开):<br>
  4. * 0 - 0000000000 0000000000 0000000000 0000000000 0 - 00000 - 00000 -
  5. * 000000000000 <br>
  6. * 1位标识,由于long基本类型在Java中是带符号的,最高位是符号位,正数是0,负数是1,所以id一般是正数,最高位是0<br>
  7. * 41位时间截(毫秒级),注意,41位时间截不是存储当前时间的时间截,而是存储时间截的差值(当前时间截 - 开始时间截)
  8. * 得到的值),这里的的开始时间截,一般是我们的id生成器开始使用的时间,由我们程序来指定的(如下下面程序IdWorker类的startTime属性)。41位的时间截,可以使用69年,年T
  9. * = (1L << 41) / (1000L * 60 * 60 * 24 * 365) = 69<br>
  10. * 10位的数据机器位,可以部署在1024个节点,包括5位datacenterId和5位workerId<br>
  11. * 12位序列,毫秒内的计数,12位的计数顺序号支持每个节点每毫秒(同一机器,同一时间截)产生4096个ID序号<br>
  12. * 加起来刚好64位,为一个Long型。<br>
  13. * SnowFlake的优点是,整体上按照时间自增排序,并且整个分布式系统内不会产生ID碰撞(由数据中心ID和机器ID作区分),并且效率较高,经测试,SnowFlake每秒能够产生26万ID左右。
  14. */
  15. public class SnowflakeId {
  16. /** 开始时间截 (2015-01-01) */
  17. private final long twepoch = 1420041600000L;
  18.  
  19. /** 机器id所占的位数 */
  20. private final long workerIdBits = 5L;
  21.  
  22. /** 数据标识id所占的位数 */
  23. private final long datacenterIdBits = 5L;
  24.  
  25. /** 支持的最大机器id,结果是31 (这个移位算法可以很快的计算出几位二进制数所能表示的最大十进制数) */
  26. private final long maxWorkerId = -1L ^ (-1L << workerIdBits);
  27.  
  28. /** 支持的最大数据标识id,结果是31 */
  29. private final long maxDatacenterId = -1L ^ (-1L << datacenterIdBits);
  30.  
  31. /** 序列在id中占的位数 */
  32. private final long sequenceBits = 12L;
  33.  
  34. /** 机器ID向左移12位 */
  35. private final long workerIdShift = sequenceBits;
  36.  
  37. /** 数据标识id向左移17位(12+5) */
  38. private final long datacenterIdShift = sequenceBits + workerIdBits;
  39.  
  40. /** 时间截向左移22位(5+5+12) */
  41. private final long timestampLeftShift = sequenceBits + workerIdBits + datacenterIdBits;
  42.  
  43. /** 生成序列的掩码,这里为4095 (0b111111111111=0xfff=4095) */
  44. private final long sequenceMask = -1L ^ (-1L << sequenceBits);
  45.  
  46. /** 工作机器ID(0~31) */
  47. private long workerId;
  48.  
  49. /** 数据中心ID(0~31) */
  50. private long datacenterId;
  51.  
  52. /** 毫秒内序列(0~4095) */
  53. private long sequence = 0L;
  54.  
  55. /** 上次生成ID的时间截 */
  56. private long lastTimestamp = -1L;
  57.  
  58. /**
  59. * @param workerId 工作ID (0~31)
  60. * @param datacenterId 数据中心ID (0~31)
  61. */
  62. public SnowflakeId(long workerId, long datacenterId) {
  63. if (workerId > maxWorkerId || workerId < 0) {
  64. throw new IllegalArgumentException(
  65. String.format("worker Id can't be greater than %d or less than 0", maxWorkerId));
  66. }
  67. if (datacenterId > maxDatacenterId || datacenterId < 0) {
  68. throw new IllegalArgumentException(
  69. String.format("datacenter Id can't be greater than %d or less than 0", maxDatacenterId));
  70. }
  71. this.workerId = workerId;
  72. this.datacenterId = datacenterId;
  73. }
  74.  
  75. /**
  76. * 获得下一个ID (该方法是线程安全的)
  77. *
  78. * @return SnowflakeId
  79. */
  80. public synchronized long nextId() {
  81. long timestamp = timeGen();
  82.  
  83. // 如果当前时间小于上一次ID生成的时间戳,说明系统时钟回退过这个时候应当抛出异常
  84. if (timestamp < lastTimestamp) {
  85. throw new RuntimeException(String.format(
  86. "Clock moved backwards. Refusing to generate id for %d milliseconds", lastTimestamp - timestamp));
  87. }
  88.  
  89. // 如果是同一时间生成的,则进行毫秒内序列
  90. if (lastTimestamp == timestamp) {
  91. sequence = (sequence + 1) & sequenceMask;
  92. // 毫秒内序列溢出
  93. if (sequence == 0) {
  94. // 阻塞到下一个毫秒,获得新的时间戳
  95. timestamp = tilNextMillis(lastTimestamp);
  96. }
  97. }
  98. // 时间戳改变,毫秒内序列重置
  99. else {
  100. sequence = 0L;
  101. }
  102.  
  103. // 上次生成ID的时间截
  104. lastTimestamp = timestamp;
  105.  
  106. // 移位并通过或运算拼到一起组成64位的ID
  107. return ((timestamp - twepoch) << timestampLeftShift) //
  108. | (datacenterId << datacenterIdShift) //
  109. | (workerId << workerIdShift) //
  110. | sequence;
  111. }
  112.  
  113. /**
  114. * 阻塞到下一个毫秒,直到获得新的时间戳
  115. *
  116. * @param lastTimestamp 上次生成ID的时间截
  117. * @return 当前时间戳
  118. */
  119. protected long tilNextMillis(long lastTimestamp) {
  120. long timestamp = timeGen();
  121. while (timestamp <= lastTimestamp) {
  122. timestamp = timeGen();
  123. }
  124. return timestamp;
  125. }
  126.  
  127. /**
  128. * 返回以毫秒为单位的当前时间
  129. *
  130. * @return 当前时间(毫秒)
  131. */
  132. protected long timeGen() {
  133. return System.currentTimeMillis();
  134. }
  135.  
  136. /** 测试 */
  137. public static void main(String[] args) {
  138. long startTime = System.currentTimeMillis();
  139. SnowflakeId snowflakeId = new SnowflakeId(0, 0);
  140. for (int i = 0; i < 1000; i++) {
  141. long id = snowflakeId.nextId();
  142. System.out.println(Long.toBinaryString(id));
  143. System.out.println(id);
  144. }
  145. long endTime = System.currentTimeMillis();
  146. System.out.println("生成1000个id的时间:" + (endTime - startTime));
  147. }
  148. }

运行结果:

...
生成1000个id的时间:16

效率非常高,1000个id才花费16ms

分布式自增ID算法snowflake的更多相关文章

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

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

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

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

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

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

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

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

  5. 基于.NET Standard的分布式自增ID算法--Snowflake

    概述 本篇文章主要讲述分布式ID生成算法中最出名的Snowflake算法.搞.NET开发的,数据库主键最常见的就是int类型的自增主键和GUID类型的uniqueidentifier. 那么为何还要引 ...

  6. Twitter的分布式自增ID算法snowflake

    snowflake 分布式场景下获取自增id git:https://github.com/twitter/snowflake 解读: http://www.cnblogs.com/relucent/ ...

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

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

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

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

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

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

随机推荐

  1. js off 缓动动画

    动画也有很多种,一起来学习,缓动动画吧 缓动动画 1.缓动动画原理=盒子位置+(目标盒子位置-现在盒子位置)/10 2.步长越来越小 3.让步长越来越小的公式      步长=(目标位置-本身位置)/ ...

  2. SignalR要求_转自:https://www.cnblogs.com/humble/p/3855137.html

    SignalR 服务端组件可以被部署在诸多的服务器配置中,本节描述了它所支持的操作系统版本,.NET framework,IIS.以及其他组件 二.支持的服务器操作系统 SignalR服务端组件可以被 ...

  3. pymongo helper

    import pymongo import click # 数据库基本信息 db_configs = { 'type': 'mongo', 'host': '127.0.0.1', 'port': ' ...

  4. Go -- client 302 自动转 200 问题 cookie存储 模拟登陆问题

    不久前用go写了个http client,去模拟某网站(*.com)的登录操作.网站的登录逻辑:1.验证登录账号和密码:2.下发token.此token通过cookie下发:3.redirect到主页 ...

  5. vue中样式被覆盖的问题

    在我们引入外部的样式时,发现自己无论如何都改不了外部的样式,自己的样式老被覆盖,究其原因还是我们的 外部样式放的位置不对 main.js 我们应该在 main.js 的开头引入样式,这样的话就不存在覆 ...

  6. OSG学习笔记0——解决OSG读obj模型问题[转]

    原文:https://blog.csdn.net/u011310341/article/details/51179948 #include "stdafx.h" #include& ...

  7. [V5] ARM: dts: Change i2s compatible string on exynos5250【转】

    本文转载自:https://patchwork.kernel.org/patch/2845464/ Padmavathi VennaAug. 16, 2013, 4:26 a.m. UTC This ...

  8. Qt Creater-特殊注释TODO,FIXME

    简述 TODO: + 说明: 如果代码中有该标识,说明在标识处有功能代码待编写,待实现的功能在说明中会简略说明. FIXME: + 说明: 如果代码中有该标识,说明标识处代码需要修正,甚至代码是错误的 ...

  9. 微信小程序-收货地址左滑删除

    我参照了其中的部分代码,如:bindtouchstart,bindtouchmove,bindtouchend事件多数组中偏移值的更改, 在结合微信 movable-area 和 movable-vi ...

  10. Apache-dbutils 简介及事务处理

    一:commons-dbutils简介 commons-dbutils 是 Apache 组织提供的一个开源 JDBC工具类库,它是对JDBC的简单封装,学习成本极低,并且使用dbutils能极大简化 ...