雪花算法(SnowFlake)Java实现

雪花算法(SnowFlake)Java实现

算法原理

SnowFlake算法生成id的结果是一个64bit大小的整数,它的结构如下图:

在这里插入图片描述

由于在Java中64bit的整数是long类型,所以在Java中SnowFlake算法生成的id就是long来存储的。

SnowFlake可以保证:

所有生成的id按时间趋势递增
整个分布式系统内不会产生重复id(因为有datacenterId和machineId来做区分)

算法实现(Java)

Twitter官方给出的算法实现 是用Scala写的,这里不做分析,可自行查看。

  1. /**
  2. ** SnowFlake的结构如下(每部分用-分开):
  3. ** 0 - 0000000000 0000000000 0000000000 0000000000 0 - 00000 - 00000 -
  4. * 000000000000
  5. ** 1位标识,由于long基本类型在Java中是带符号的,最高位是符号位,正数是0,负数是1,所以id一般是正数,最高位是0
  6. ** 41位时间截(毫秒级),注意,41位时间截不是存储当前时间的时间截,而是存储时间截的差值(当前时间截 - 开始时间截)
  7. ** 得到的值),这里的的开始时间截,一般是我们的id生成器开始使用的时间,由我们程序来指定的(如下下面程序IdWorker类的startTime属性)。41位的时间截,可以使用69年,年T
  8. * = (1L << 41) / (1000L * 60 * 60 * 24 * 365) = 69
  9. ** 10位的数据机器位,可以部署在1024个节点,包括5位datacenterId和5位workerId<br>
  10. ** 12位序列,毫秒内的计数,12位的计数顺序号支持每个节点每毫秒(同一机器,同一时间截)产生4096个ID序号
  11. ** 加起来刚好64位,为一个Long型。
  12. **
  13. * SnowFlake的优点是,整体上按照时间自增排序,并且整个分布式系统内不会产生ID碰撞(由数据中心ID和机器ID作区分),并且效率较高,经测试,SnowFlake每秒能够产生20多万ID左右。
  14. * @author byran
  15. **/
  16. public class SnowflakeIdWorker {
  17. private static final Logger logger = LoggerFactory.getLogger(SnowflakeIdWorker.class);
  18. /**
  19. * 起始的时间戳
  20. **/
  21. private final static long START_STMP = 1480166465631L;
  22. /**
  23. * 每一部分占用的位数
  24. **/
  25. private final static long SEQUENCE_BIT = 10; //序列号占用的位数
  26. private final static long MACHINE_BIT = 5; //机器标识占用的位数
  27. private final static long DATACENTER_BIT = 5;//数据中心占用的位数
  28. /**
  29. * 每一部分的最大值
  30. **/
  31. public final static long MAX_DATACENTER_NUM = -1L ^ (-1L << DATACENTER_BIT);
  32. public final static long MAX_MACHINE_NUM = -1L ^ (-1L << MACHINE_BIT);
  33. private final static long MAX_SEQUENCE = -1L ^ (-1L << SEQUENCE_BIT);
  34. /**
  35. * 每一部分向左的位移
  36. **/
  37. private final static long MACHINE_LEFT = SEQUENCE_BIT;
  38. private final static long DATACENTER_LEFT = SEQUENCE_BIT + MACHINE_BIT;
  39. private final static long TIMESTMP_LEFT = DATACENTER_LEFT + DATACENTER_BIT;
  40. private long datacenterId; //数据中心
  41. private long machineId; //机器标识
  42. private long sequence = 0L; //序列号
  43. private long lastStmp = -1L;//上一次时间戳
  44. /**
  45. * 根据MAC生成datacenterId,根据MAC + PID生成machineId
  46. **/
  47. public SnowflakeIdWorker() {
  48. long datacenterId = getDatacenterId(MAX_DATACENTER_NUM);
  49. long machineId = getMachineId(datacenterId, MAX_MACHINE_NUM);
  50. check(datacenterId, machineId);
  51. this.datacenterId = datacenterId;
  52. this.machineId = machineId;
  53. }
  54. /**
  55. * datacenterId和machineId可配置
  56. * @param datacenterId
  57. * @param machineId
  58. **/
  59. public SnowflakeIdWorker(long datacenterId, long machineId) {
  60. check(datacenterId, machineId);
  61. this.datacenterId = datacenterId;
  62. this.machineId = machineId;
  63. }
  64. private static void check(long datacenterId, long machineId) {
  65. if (datacenterId > MAX_DATACENTER_NUM || datacenterId < 0) {
  66. throw new EompRuntimeException(String.format("datacenterId can't be greater than %s or less than 0", MAX_DATACENTER_NUM));
  67. }
  68. if (machineId > MAX_MACHINE_NUM || machineId < 0) {
  69. throw new EompRuntimeException(String.format("machineId can't be greater than %s or less than 0", MAX_MACHINE_NUM));
  70. }
  71. }
  72. /**
  73. * 产生下一个ID
  74. * @return
  75. **/
  76. public synchronized long nextId() {
  77. long currStmp = getNewstmp();
  78. if (currStmp < lastStmp) {
  79. throw new EompRuntimeException("Clock moved backwards. Refusing to generate id");
  80. }
  81. if (currStmp == lastStmp) {
  82. //相同毫秒内,序列号自增
  83. sequence = (sequence + 1) & MAX_SEQUENCE;
  84. //同一毫秒的序列数已经达到最大
  85. if (sequence == 0L) {
  86. currStmp = getNextMill();
  87. }
  88. } else {
  89. //不同毫秒内,序列号置为0
  90. sequence = 0L;
  91. }
  92. lastStmp = currStmp;
  93. return (currStmp - START_STMP) << TIMESTMP_LEFT //时间戳部分
  94. | datacenterId << DATACENTER_LEFT //数据中心部分
  95. | machineId << MACHINE_LEFT //机器标识部分
  96. | sequence; //序列号部分
  97. }
  98. /**
  99. * 阻塞到下一个毫秒,直到获得新的时间戳
  100. * @return 当前时间戳
  101. **/
  102. private long getNextMill() {
  103. long mill = getNewstmp();
  104. while (mill <= lastStmp) {
  105. mill = getNewstmp();
  106. }
  107. return mill;
  108. }
  109. /**
  110. * 返回以毫秒为单位的当前时间
  111. * @return 当前时间(毫秒)
  112. **/
  113. private long getNewstmp() {
  114. return System.currentTimeMillis();
  115. }
  116. /**
  117. * 机器标识
  118. **/
  119. private static long getMachineId(long datacenterId, long maxWorkerId) {
  120. StringBuilder mpid = new StringBuilder();
  121. mpid.append(datacenterId);
  122. String name = ManagementFactory.getRuntimeMXBean().getName();
  123. if (!name.isEmpty()) {
  124. /** GET jvmPid */
  125. mpid.append(name.split("@")[0]);
  126. }
  127. /** MAC + PID 的 hashcode 获取16个低位 */
  128. return (mpid.toString().hashCode() & 0xffff) % (maxWorkerId + 1);
  129. }
  130. /**
  131. * 数据标识id部分
  132. **/
  133. private static long getDatacenterId(long maxDatacenterId) {
  134. long id = 0L;
  135. try {
  136. InetAddress ip = InetAddress.getLocalHost();
  137. NetworkInterface network = NetworkInterface.getByInetAddress(ip);
  138. if (network == null) {
  139. id = 1L;
  140. } else {
  141. byte[] mac = network.getHardwareAddress();
  142. id = ((0x000000FF & (long) mac[mac.length - 1])
  143. | (0x0000FF00 & (((long) mac[mac.length - 2]) << 8))) >> 6;
  144. id = id % (maxDatacenterId + 1);
  145. }
  146. } catch (Exception e) {
  147. logger.error("getDatacenterId exception.", e);
  148. }
  149. return id;
  150. }
  151. /*public static void main(String[] args) {
  152. long datacenterId = getDatacenterId(MAX_DATACENTER_NUM);
  153. long machineId = getMachineId(datacenterId, MAX_MACHINE_NUM);
  154. System.out.println("ip:" + datacenterId + ",processId:" + machineId);
  155. }*/
  156. }

测试类:

  1. public class SnowflakeIdWorkerTest {
  2. public static Set<Long> idSet = new HashSet<>();
  3. public static void main(String[] args) {
  4. SnowflakeIdWorker snowflakeIdWorker = new SnowflakeIdWorker(1, 0);
  5. for (long i = 0; i < 1000; i++) {
  6. new Thread(new Worker(snowflakeIdWorker)).start();
  7. }
  8. }
  9. static class Worker implements Runnable {
  10. private SnowflakeIdWorker snowflakeIdWorker;
  11. public Worker(SnowflakeIdWorker snowflakeIdWorker) {
  12. this.snowflakeIdWorker = snowflakeIdWorker;
  13. }
  14. @Override
  15. public void run() {
  16. for (int i = 0; i < 1000; i++) {
  17. Long id = snowflakeIdWorker.nextId();
  18. if (!idSet.add(id)) {
  19. System.err.println("存在重复id:" + id);
  20. }
  21. }
  22. }
  23. }
  24. }

发表评论

表情:
评论列表 (有 0 条评论,89人围观)

还没有评论,来说两句吧...

相关阅读

    相关 雪花算法

    雪花算法生成的ID可用于做分布式系统ID 生成的ID格式 1位标识符(始终是0)+41位时间戳 + 10位机器标识符(5位datacenterId+5位WorkId) +12

    相关 雪花算法

    我们都知道在一个分布式系统中生成一个无重复的标识是非常重要的,业界也有很多算法。 雪花(snowflake)在自然界中,是极具独特美丽,又变幻莫测的东西: 1. 雪花属于

    相关 雪花算法

    雪花算法 `雪花算法`的原始版本是scala版,用于生成分布式ID(纯数字,时间顺序),订单编号等。 结构: ![在这里插入图片描述][watermark_type