/**
* Twitter_Snowflake<br>
* SnowFlake的结构如下(每部分用-分开):<br>
* 0 - 0000000000 0000000000 0000000000 0000000000 0 - 00000 - 00000 - 000000000000 <br>
* 1位标识,由于long基本类型在Java中是带符号的,最高位是符号位,正数是0,负数是1,所以id一般是正数,最高位是0<br>
* 41位时间截(毫秒级),注意,41位时间截不是存储当前时间的时间截,而是存储时间截的差值(当前时间截 - 开始时间截)
* 得到的值),这里的的开始时间截,一般是我们的id生成器开始使用的时间,由我们程序来指定的(如下下面程序IdWorker类的startTime属性)。41位的时间截,可以使用69年,年T = (1L << 41) / (1000L * 60 * 60 * 24 * 365) = 69<br>
* 10位的数据机器位,可以部署在1024个节点,包括5位datacenterId和5位workerId<br>
* 12位序列,毫秒内的计数,12位的计数顺序号支持每个节点每毫秒(同一机器,同一时间截)产生4096个ID序号<br>
* 加起来刚好64位,为一个Long型。<br>
* SnowFlake的优点是,整体上按照时间自增排序,并且整个分布式系统内不会产生ID碰撞(由数据中心ID和机器ID作区分),并且效率较高,经测试,SnowFlake每秒能够产生26万ID左右。
*/

  1. package cn.ucaner.alpaca.common.util.key;
  2.  
  3. /**
  4. * Twitter_Snowflake<br>
  5. * SnowFlake的结构如下(每部分用-分开):<br>
  6. * 0 - 0000000000 0000000000 0000000000 0000000000 0 - 00000 - 00000 - 000000000000 <br>
  7. * 1位标识,由于long基本类型在Java中是带符号的,最高位是符号位,正数是0,负数是1,所以id一般是正数,最高位是0<br>
  8. * 41位时间截(毫秒级),注意,41位时间截不是存储当前时间的时间截,而是存储时间截的差值(当前时间截 - 开始时间截)
  9. * 得到的值),这里的的开始时间截,一般是我们的id生成器开始使用的时间,由我们程序来指定的(如下下面程序IdWorker类的startTime属性)。41位的时间截,可以使用69年,年T = (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 SnowflakeIdWorker {
  16. // ==============================Fields===========================================
  17. /**
  18. * 开始时间截 (2015-01-01)
  19. */
  20. private final long twepoch = 1420041600000L;
  21.  
  22. /**
  23. * 机器id所占的位数
  24. */
  25. private final long workerIdBits = 5L;
  26.  
  27. /**
  28. * 数据标识id所占的位数
  29. */
  30. private final long datacenterIdBits = 5L;
  31.  
  32. /**
  33. * 支持的最大机器id,结果是31 (这个移位算法可以很快的计算出几位二进制数所能表示的最大十进制数)
  34. */
  35. private final long maxWorkerId = -1L ^ (-1L << workerIdBits);
  36.  
  37. /**
  38. * 支持的最大数据标识id,结果是31
  39. */
  40. private final long maxDatacenterId = -1L ^ (-1L << datacenterIdBits);
  41.  
  42. /**
  43. * 序列在id中占的位数
  44. */
  45. private final long sequenceBits = 12L;
  46.  
  47. /**
  48. * 机器ID向左移12位
  49. */
  50. private final long workerIdShift = sequenceBits;
  51.  
  52. /**
  53. * 数据标识id向左移17位(12+5)
  54. */
  55. private final long datacenterIdShift = sequenceBits + workerIdBits;
  56.  
  57. /**
  58. * 时间截向左移22位(5+5+12)
  59. */
  60. private final long timestampLeftShift = sequenceBits + workerIdBits + datacenterIdBits;
  61.  
  62. /**
  63. * 生成序列的掩码,这里为4095 (0b111111111111=0xfff=4095)
  64. */
  65. private final long sequenceMask = -1L ^ (-1L << sequenceBits);
  66.  
  67. /**
  68. * 工作机器ID(0~31)
  69. */
  70. private long workerId;
  71.  
  72. /**
  73. * 数据中心ID(0~31)
  74. */
  75. private long datacenterId;
  76.  
  77. /**
  78. * 毫秒内序列(0~4095)
  79. */
  80. private long sequence = 0L;
  81.  
  82. /**
  83. * 上次生成ID的时间截
  84. */
  85. private long lastTimestamp = -1L;
  86.  
  87. //==============================Constructors=====================================
  88.  
  89. /**
  90. * 构造函数
  91. *
  92. * @param workerId 工作ID (0~31)
  93. * @param datacenterId 数据中心ID (0~31)
  94. */
  95. public SnowflakeIdWorker(long workerId, long datacenterId) {
  96. if (workerId > maxWorkerId || workerId < 0) {
  97. throw new IllegalArgumentException(String.format("worker Id can't be greater than %d or less than 0", maxWorkerId));
  98. }
  99. if (datacenterId > maxDatacenterId || datacenterId < 0) {
  100. throw new IllegalArgumentException(String.format("datacenter Id can't be greater than %d or less than 0", maxDatacenterId));
  101. }
  102. this.workerId = workerId;
  103. this.datacenterId = datacenterId;
  104. }
  105.  
  106. // ==============================Methods==========================================
  107.  
  108. /**
  109. * 获得下一个ID (该方法是线程安全的)
  110. *
  111. * @return SnowflakeId
  112. */
  113. public synchronized long nextId() {
  114. long timestamp = timeGen();
  115.  
  116. //如果当前时间小于上一次ID生成的时间戳,说明系统时钟回退过这个时候应当抛出异常
  117. if (timestamp < lastTimestamp) {
  118. throw new RuntimeException(
  119. String.format("Clock moved backwards. Refusing to generate id for %d milliseconds", lastTimestamp - timestamp));
  120. }
  121.  
  122. //如果是同一时间生成的,则进行毫秒内序列
  123. if (lastTimestamp == timestamp) {
  124. sequence = (sequence + 1) & sequenceMask;
  125. //毫秒内序列溢出
  126. if (sequence == 0) {
  127. //阻塞到下一个毫秒,获得新的时间戳
  128. timestamp = tilNextMillis(lastTimestamp);
  129. }
  130. }
  131. //时间戳改变,毫秒内序列重置
  132. else {
  133. sequence = 0L;
  134. }
  135.  
  136. //上次生成ID的时间截
  137. lastTimestamp = timestamp;
  138.  
  139. //移位并通过或运算拼到一起组成64位的ID
  140. return ((timestamp - twepoch) << timestampLeftShift) //
  141. | (datacenterId << datacenterIdShift) //
  142. | (workerId << workerIdShift) //
  143. | sequence;
  144. }
  145.  
  146. /**
  147. * 阻塞到下一个毫秒,直到获得新的时间戳
  148. *
  149. * @param lastTimestamp 上次生成ID的时间截
  150. * @return 当前时间戳
  151. */
  152. protected long tilNextMillis(long lastTimestamp) {
  153. long timestamp = timeGen();
  154. while (timestamp <= lastTimestamp) {
  155. timestamp = timeGen();
  156. }
  157. return timestamp;
  158. }
  159.  
  160. /**
  161. * 返回以毫秒为单位的当前时间
  162. *
  163. * @return 当前时间(毫秒)
  164. */
  165. protected long timeGen() {
  166. //return System.currentTimeMillis();
  167. return SystemClock.now();
  168. }
  169.  
  170. //==============================Test=============================================
  171.  
  172. /**
  173. * 测试
  174. */
  175. public static void main(String[] args) {
  176. long start = System.currentTimeMillis();
  177. SnowflakeIdWorker idWorker0 = new SnowflakeIdWorker(0, 0);
  178. for (int i = 0; i < 10; i++) {
  179. long id = idWorker0.nextId();
  180. System.out.println(id);
  181. }
  182. System.out.println("耗时:" + (System.currentTimeMillis() - start));
  183. }
  184.  
  185. }
  186. //Outputs
  187. //444159897148325888
  188. //444159897148325889
  189. //444159897148325890
  190. //444159897148325891
  191. //444159897148325892
  192. //444159897148325893
  193. //444159897148325894
  194. //444159897148325895
  195. //444159897148325896
  196. //444159897148325897
  197. //耗时:7
  198.  
  199. //444159955377848320
  200. //444159955377848321
  201. //444159955377848322
  202. //444159955377848323
  203. //444159955377848324
  204. //444159955377848325
  205. //444159955377848326
  206. //444159955377848327
  207. //444159955377848328
  208. //444159955377848329
  209. //耗时:7

SnowflakeIdWorker的更多相关文章

  1. 高并发场景下System.currentTimeMillis()的性能问题的优化 以及SnowFlakeIdWorker高性能ID生成器

    package xxx; import java.sql.Timestamp; import java.util.concurrent.*; import java.util.concurrent.a ...

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

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

  3. 分布式唯一id:snowflake算法思考

    匠心零度 转载请注明原创出处,谢谢! 缘起 为什么会突然谈到分布式唯一id呢?原因是最近在准备使用RocketMQ,看看官网介绍: 一句话,消息可能会重复,所以消费端需要做幂等.为什么消息会重复后续R ...

  4. java 分布式id生成算法

    import java.lang.management.ManagementFactory; import java.net.InetAddress; import java.net.NetworkI ...

  5. ID 生成器 雪花算法

    https://blog.csdn.net/wangming520liwei/article/details/80843248 ID 生成器 雪花算法 2018年06月28日 14:58:43 wan ...

  6. SnowFlake学习

    分布式系统中生成全局唯一且趋势递增ID UUID - 太长,无序,数据库插入分裂性能不行 利用数据库自增序列,等步长生成 - 依赖数据库 SnowFlake:使用见下图 抄代码 https://www ...

  7. 唯一ID算法之:snowflake(Java版本)

    Twitter开源的算法,简单易用. /** * Twitter_Snowflake<br> * SnowFlake的结构如下(每部分用-分开):<br> * 0 - 0000 ...

  8. UUID实现之一twitter的分布式自增IDsnowflake算法

    Twitter的分布式自增ID算法snowflake (Java版)   概述 分布式系统中,有一些需要使用全局唯一ID的场景,这种时候为了防止ID冲突可以使用36位的UUID,但是UUID有一些缺点 ...

  9. Twitter的SnowFlake分布式id生成算法

    二进制相关知识回顾 1.所有的数据都是以二进制的形式存储在硬盘上.对于一个字节的8位到底是什么类型 计算机是如何分辨的呢? 其实计算机并不负责判断数据类型,数据类型是程序告诉计算机该如何解释内存块. ...

随机推荐

  1. SpringBoot之AOP使用

    说到SpringBoot,难免会想到Spring.对于Spring,我曾用其开发过很多大大小小的项目.当使用SpringBoot之后,给人最直观的感受,用古人一句话:”大道至简”. SpringBoo ...

  2. elasticsearch-py 解决 too_long_frame_exception 问题

    elasticsearch-py 解决 too_long_frame_exception 问题 老大让我搞一搞数据统计,配环境时遇到个奇葩错误,记录一下,希望能帮助到某些人. 我需要安装 Elasti ...

  3. BGP MPLS IP V匹N基本概念

    BGP/MPLS IP VPN基本概念 Site 在介绍VPN时经常会提到"Site",Site(站点)的含义可以从下述几个方面理解: · Site是指相互之间具备IP连通性的一组 ...

  4. Java学习-055-Jsoup爬虫通过设置获取响应数据大小的最大值,解决因默认获取 1MB 响应数据导致的无法获取全部的响应数据内容问题

    在日常工作中,通常会遇到获取各种网络数据使用的情况,Java中可使用Jsoup(Python中可使用 BeatifulSoup)进行数据的获取及处理. 今天有朋友问,在使用 Jsoup 进行请求数据时 ...

  5. bash命令检测Shell脚本中的语法错误和查看详细执行过程

    (1).bash命令检测Shell脚本中的语法错误 bash -v [脚本] [root@youxi1 ~]# vim a.sh #/bin/bash sum=$[$1+$2] echoo $sum ...

  6. Ubuntu16.04安装Consul

    1.下载安装包 https://www.consul.io/downloads.html wget https://releases.hashicorp.com/consul/1.5.3/consul ...

  7. SpringBoot处理异常方式

    SpringBoot提供了多种处理异常方式,以下为常用的几种 1. 自定义错误异常页面 SpringBoot默认的处理异常的机制:SpringBoot默认的已经提供了一套处理异常的机制.一旦程序中出现 ...

  8. hive 引入第三方包(不重启)

    Jar放入${HIVE_HOME}/auxlib目录 在${HIVE_HOME}中创建文件夹auxlib,然后将自定义jar文件放入该文件夹中. 此方法添加不需要重启Hive.而且比较便捷. 连接方式 ...

  9. 使用极光第三方IM的时候服务器报错Caused by: java.net.UnknownHostException: api.im.jpush.cn

    Caused by: java.net.UnknownHostException: api.im.jpush.cn 服务器报这个:首先查看服务器是否能ping通,如果能ping通,则看下 vi /et ...

  10. SignalR长连接的简单用法

    ASP.NET SignalR 是为 ASP.NET 开发人员提供的一个库,可以简化开发人员将实时 Web 功能添加到应用程序的过程.实时 Web 功能是指这样一种功能:当所连接的客户端变得可用时服务 ...