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

生成ID之雪花算法的更多相关文章

  1. ID 生成器 雪花算法

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

  2. 分布式ID的雪花算法及坑

    分布式ID生成是目前系统的常见刚需,其中以Twitter的雪花算法(Snowflake)比较知名,有Java等各种语言的版本及各种改进版本,能生成满足分布式ID,返回ID为Long长整数 但是这里有一 ...

  3. 适用于分布式ID的雪花算法

    基于Java实现的适用于分布式ID的雪花算法工具类,这里存一下日后好找 /** * 雪花算法生成ID */ public class SnowFlakeUtil { private final sta ...

  4. 生成主键ID,唯一键id,分布式ID生成器雪花算法代码实现

    工具类:  package com.ihrm.common.utils; import java.lang.management.ManagementFactory; import java.net. ...

  5. 唯一ID生成器--雪花算法

    在微服务架构,分布式系统中的操作会有一些全局性ID的需求,所以我们不能用数据库本身的自增功能来产生主键值,只能由程序来生成唯一的主键值.我们采用的是twitter的snokeflake(雪花)算法. ...

  6. 全局ID生成--雪花算法

    分布式ID常见生成策略: 分布式ID生成策略常见的有如下几种: 数据库自增ID. UUID生成. Redis的原子自增方式. 数据库水平拆分,设置初始值和相同的自增步长. 批量申请自增ID. 雪花算法 ...

  7. 雪花算法生成分布式ID

    分布式主键ID生成方案 分布式主键ID的生成方案有以下几种: 数据库自增主键 缺点: 导入旧数据时,可能会ID重复,导致导入失败 分布式架构,多个Mysql实例可能会导致ID重复 UUID 缺点: 占 ...

  8. Snowflake(雪花算法),什么情况下会冲突?

    文章首发在公众号(龙台的技术笔记),之后同步到博客园和个人网站:xiaomage.info 分布式系统中,有一些需要使用全局唯一 ID 的场景,这种时候为了防止 ID 冲突可以使用 36 位的 UUI ...

  9. 分布式系统-主键唯一id,订单编号生成-雪花算法-SnowFlake

    分布式系统下 我们每台设备(分布式系统-独立的应用空间-或者docker环境) * SnowFlake的优点是,整体上按照时间自增排序,并且整个分布式系统内不会产生ID碰撞(由数据中心ID和机器ID作 ...

随机推荐

  1. CentOS 7下Cloudera Manager及CDH 6.0.1安装过程详解

    目录 一.概念介绍 1.CDH 概览 2.Cloudera Manager 概览 二.环境准备 1.软件版本选择 2.节点准备(四个节点) 3.配置主机名和hosts解析(所有节点) 4.关闭防火墙 ...

  2. 微信小程序 左右分类列表

    分类界面,左边是一级目录,右边是一级目录对应的二级目录,根据这个需求,我们数据设计的结构一定是数组嵌套数组,第一个数组包含一级目录数据,嵌套的数组包含的是二级目录的数据. wxml代码: <vi ...

  3. 利用先电云iaas平台搭建apache官方大数据平台(ambari2.7+hdp3.0)

    一.ambari架构解析 二.基础环境配置 以两台节点为例来组件Hadoop分布式集群,这里采用的系统版本为Centos7 1511,如下表所示: 主机名 内存 硬盘 IP地址 角色 master 8 ...

  4. 日期控件传到后台异常。日期数据格式是 Date 还是 String?

    问题:日期控件的时间,传到Controller层直接异常. 前台日期格式:YYYY/MM/DD,后台Java定义的时间类型:Date. 解决: 方法一:原因是Controller层的参数类型定义为 D ...

  5. 使用Dockerfile创建ssh服务的镜像02

    使用Dockerfile创建ssh服务的镜像02 1:创建工作目录---一个镜像的所有文件都放这个目录下 ubuntu@ubuntu:~$ mkdir sshd_ubuntu ubuntu@ubunt ...

  6. luogu P1216 [IOI1994][USACO1.5]数字三角形 Number Triangles (递推)

    链接:https://www.luogu.org/problemnew/show/P1216 题面: 题目描述 观察下面的数字金字塔. 写一个程序来查找从最高点到底部任意处结束的路径,使路径经过数字的 ...

  7. [转帖]密钥库文件格式(Keystore)和证书文件格式(Certificate)

    密钥库文件格式[keystore]代码 https://blog.csdn.net/zzhongcy/article/details/22755317 格式 : JKS 扩展名 : .jks/.ks ...

  8. 在Ubuntu中搭建Python3的虚拟环境并开始django项目

    搭建环境: 1.首先安装virtualenv: pip install virtualenv 2.创建虚拟环境:(指定安装Python3,若不写-p python3,默认安装Python2.7),en ...

  9. 【LOJ】#3103. 「JSOI2019」节日庆典

    LOJ#3103. 「JSOI2019」节日庆典 能当最小位置的值一定是一个最小后缀,而有用的最小后缀不超过\(\log n\)个 为什么不超过\(\log n\)个,看了一下zsy的博客.. 假如\ ...

  10. 网络编程之异步IO

    Linux的I/O模型有下面几种:1. 同步阻塞I/O: 用户进程进行I/O操作,一直阻塞到I/O操作完成为止.2. 同步非阻塞I/O: 用户程序可以通过设置文件描述符的属性O_NONBLOCK,I/ ...