码迷,mamicode.com
首页 > 编程语言 > 详细

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

时间:2018-12-19 15:37:48      阅读:214      评论:0      收藏:0      [点我收藏+]

标签:enter   sql迁移   mysql   lse   extern   场景   git   sequence   ora   

概述

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

有些时候我们希望能使用一种简单一些的ID,并且希望ID能够按照时间有序生成。

而twitter的snowflake解决了这种需求,最初Twitter把存储系统从MySQL迁移到Cassandra,因为Cassandra没有顺序ID生成机制,所以开发了这样一套全局唯一ID生成服务。

 

结构

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

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

第一位为未使用,接下来的41位为毫秒级时间(41位的长度可以使用69年),然后是5位datacenterId和5位workerId(10位的长度最多支持部署1024个节点) ,最后12位是毫秒内的计数(12位的计数顺序号支持每个节点每毫秒产生4096个ID序号)

一共加起来刚好64位,为一个Long型。(转换成字符串后长度最多19)

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

源码

(JAVA版本的源码)

  1 package com.xazhxc.htjcom.back.controller.base;
  2 
  3 import lombok.extern.slf4j.Slf4j;
  4 
  5 /**
  6  * 原作者 zzxadi https://github.com/zzxadi/Snowflake-IdWorker
  7  * @author Exrickx
  8  */
  9 @Slf4j
 10 public class SnowFlakeUtil {
 11 
 12     private final long id;
 13     /**
 14      * 时间起始标记点,作为基准,一般取系统的最近时间
 15      */
 16     private final long epoch = 1524291141010L;
 17     /**
 18      * 机器标识位数
 19      */
 20     private final long workerIdBits = 10L;
 21     /**
 22      * 机器ID最大值: 1023
 23      */
 24     private final long maxWorkerId = -1L ^ -1L << this.workerIdBits;
 25     /**
 26      * 0,并发控制
 27      */
 28     private long sequence = 0L;
 29     /**
 30      * 毫秒内自增位
 31      */
 32     private final long sequenceBits = 12L;
 33 
 34     /**
 35      * 12
 36      */
 37     private final long workerIdShift = this.sequenceBits;
 38     /**
 39      * 22
 40      */
 41     private final long timestampLeftShift = this.sequenceBits + this.workerIdBits;
 42     /**
 43      * 4095,111111111111,12位
 44      */
 45     private final long sequenceMask = -1L ^ -1L << this.sequenceBits;
 46     private long lastTimestamp = -1L;
 47 
 48     private SnowFlakeUtil(long id) {
 49         if (id > this.maxWorkerId || id < 0) {
 50             throw new IllegalArgumentException(String.format("worker Id can‘t be greater than %d or less than 0", this.maxWorkerId));
 51         }
 52         this.id = id;
 53     }
 54 
 55     public synchronized long nextId() {
 56         long timestamp = timeGen();
 57         if (this.lastTimestamp == timestamp) {
 58             //如果上一个timestamp与新产生的相等,则sequence加一(0-4095循环); 对新的timestamp,sequence从0开始
 59             this.sequence = this.sequence + 1 & this.sequenceMask;
 60             if (this.sequence == 0) {
 61                 // 重新生成timestamp
 62                 timestamp = this.tilNextMillis(this.lastTimestamp);
 63             }
 64         } else {
 65             this.sequence = 0;
 66         }
 67 
 68         if (timestamp < this.lastTimestamp) {
 69             log.error(String.format("clock moved backwards.Refusing to generate id for %d milliseconds", (this.lastTimestamp - timestamp)));
 70             return -1;
 71         }
 72 
 73         this.lastTimestamp = timestamp;
 74         return timestamp - this.epoch << this.timestampLeftShift | this.id << this.workerIdShift | this.sequence;
 75     }
 76 
 77     private static SnowFlakeUtil flowIdWorker = new SnowFlakeUtil(1);
 78     public static SnowFlakeUtil getFlowIdInstance() {
 79         return flowIdWorker;
 80     }
 81 
 82     /**
 83      * 等待下一个毫秒的到来, 保证返回的毫秒数在参数lastTimestamp之后
 84      */
 85     private long tilNextMillis(long lastTimestamp) {
 86         long timestamp = timeGen();
 87         while (timestamp <= lastTimestamp) {
 88             timestamp = timeGen();
 89         }
 90         return timestamp;
 91     }
 92 
 93     /**
 94      * 获得系统当前毫秒数
 95      */
 96     private static long timeGen() {
 97         return System.currentTimeMillis();
 98     }
 99 
100     public static void main(String[] args) {
101         for(int i=0;i<100;i++){
102             SnowFlakeUtil snowFlakeUtil = SnowFlakeUtil.getFlowIdInstance();
103             System.out.println(snowFlakeUtil.nextId());
104         }
105     }
106 }

 

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

标签:enter   sql迁移   mysql   lse   extern   场景   git   sequence   ora   

原文地址:https://www.cnblogs.com/joyny/p/10142986.html

(0)
(0)
   
举报
评论 一句话评论(0
登录后才能评论!
© 2014 mamicode.com 版权所有  联系我们:gaon5@hotmail.com
迷上了代码!