Twitter的分布式系统中ID生成方法——Snowflake

时间:2022-12-22 07:25:47
Twitter-Snowflake算法产生的背景相当简单,为了满足Twitter每秒上万条消息的请求,每条消息都必须分配一条唯一的id,这些id还需要一些大致的顺序(方便客户端排序),并且在分布式系统中不同机器产生的id必须不同。
Snowflake 核心代码:
  1 /**
  2  * 
  3  */
  4 package utility;
  5 
  6 /**
  7  * @author Lumin(At Home)
  8  * Twitter's Concurrent Id Generator -- SnowFlake
  9  * 0 - 0000000000 0000000000 0000000000 0000000000 0 - 00000 - 00000 - 000000000000
 10  * 1位标识,由于long基本类型在Java中是带符号的,最高位是符号位,正数是0,负数是1,所以id一般是正数,最高位是0;
 11  * 41位时间截(毫秒级),注意,41位时间截不是存储当前时间的时间截,而是存储时间截的差值(当前时间截 - 开始时间截)
 12  * 得到的值),这里的的开始时间截,一般是我们的id生成器开始使用的时间,由我们程序来指定的(如下下面程序IdWorker
 13  * 类的startTime属性)。41位的时间截,可以使用69年,即 T = (1L << 41) / (1000L * 60 * 60 * 24 * 365) = 69.
 14  * 10位的数据机器位,可以部署在1024个节点,包括:
 15  *     5位datacenter Id:数据中心机器号
 16  *     5位worker Id:工作机器号,预分配不重复的ID
 17  * 12位序列,毫秒内的计数,12位的计数顺序号支持每个节点每毫秒(同一机器,同一时间截)产生4096个ID序号
 18  * SnowFlake的优点是,整体上按照时间自增排序,并且整个分布式系统内不会产生ID碰撞(由数据中心ID和机器ID作区分),
 19  * 并且效率较高,经测试,SnowFlake每秒能够产生26万ID左右。
 20  */
 21 public class SnowFlakeIdGenerator {
 22     //开始时间戳:2015-01-01
 23     private final long twitEpoch = 1420041600000L;
 24     
 25     //机器ID的位数:5
 26     private final long workerIdBits = 5L;
 27     
 28     //数据中心ID的位数:5
 29     private final long datacenterIdBits = 5L;
 30     
 31     //机器ID的最大值:31
 32     private final long maxWorkerId = -1L ^ (-1L << workerIdBits);
 33     
 34     //数据中心ID的最大值:31
 35     private final long maxDatacenterId = -1L ^ ( - 1L << datacenterIdBits);
 36     
 37     //序列所占的位数:12
 38     private final long sequenceBits = 12L;
 39     
 40     //机器ID的偏移位数:12
 41     private final long workerIdShift = sequenceBits;
 42     
 43     //数据中心的偏移位数:12 + 5 = 17
 44     private final long datacenterIdShift = sequenceBits + workerIdBits;
 45     
 46     //时间戳的偏移位数:12 + 5 + 5 = 22
 47     private final long timestampLeftShift = sequenceBits + workerIdBits + datacenterIdBits;
 48     
 49     //生成序列的掩码,这里为:0b111111111111=0xfff=4095
 50     private final long sequenceMask = -1L ^ (-1L << sequenceBits);
 51     
 52     //工作机器的ID
 53     private long workerId;
 54     
 55     //数据中心的ID
 56     private long datacenterId;
 57     
 58     //毫秒内的序列值
 59     private long sequence = 0L;
 60     
 61     //上一次生成序列的时间戳
 62     private long lastTimestamp = -1L;
 63     
 64     /***
 65      * 构造器
 66      * @param workerId 工作机器的ID
 67      * @param datacenterId 数据中心的ID
 68      */
 69     public SnowFlakeIdGenerator(long workerId, long datacenterId) {
 70         if (workerId > maxWorkerId || workerId < 0) {
 71             throw new IllegalArgumentException(String.format("worker Id cannot be greater than %d or less than 0", maxWorkerId));
 72         }
 73         if (datacenterId > maxDatacenterId || datacenterId < 0) {
 74             throw new IllegalArgumentException(String.format("datacenter Id cannot be greater than %d or less than 0", maxDatacenterId));
 75         }
 76         this.workerId = workerId;
 77         this.datacenterId = datacenterId;
 78     }
 79     
 80     /***
 81      * 获取下一个ID(线程安全的)
 82      * @return SnowFlakeID
 83      */
 84     public synchronized long getNextId() {
 85         //获取时间戳
 86         long timestamp = getTimeStamp();
 87         
 88         //当前时间戳小于上一次分配时的时间戳,证明系统时钟经过了回退,此时直接抛出异常即可
 89         if (timestamp < lastTimestamp) {
 90             throw new RuntimeException(String.format("clock moved backwards. Refused to generate id for %d milliseconds", lastTimestamp));
 91         }
 92         
 93         //如果是同一个时间戳内,则进行毫秒内的序列生成
 94         if (lastTimestamp == timestamp) {
 95             sequence = (sequence + 1) & sequenceMask;
 96             if (sequence == 0) {
 97                 //当前毫秒内的序列号用完了,等待至下一秒再分配
 98                 timestamp = waitForNextMillis(lastTimestamp);
 99             }
100         // 时间戳改变,重置毫秒内的序列为0
101         } else {
102             sequence  = 0L;
103         }
104         
105         lastTimestamp = timestamp;
106         
107         //拼接ID
108         return ((timestamp - twitEpoch) << timestampLeftShift) 
109                 | (datacenterId << datacenterIdShift)
110                 | (workerId << workerIdShift)
111                 | sequence;
112     }
113     
114     /***
115      * 用循环进行当前毫秒的阻塞,直至新的时间戳
116      * @param lastTimestamp 上一次生成ID的时间戳
117      * @return 新的时间戳
118      */
119     protected long waitForNextMillis(long lastTimestamp) {
120         long timestamp = getTimeStamp();
121         while(timestamp <= lastTimestamp) {
122             timestamp = getTimeStamp();
123         }
124         return timestamp;
125     }
126     
127     /***
128      * 当前时间
129      * @return 以毫秒为单位的当前时间
130      */
131     protected long getTimeStamp() {
132         return System.currentTimeMillis() / 1000; //除以1000是为了将毫秒放大到秒,便于观察ID溢出 133     }
134 }
测试:
 1 package test;
 2 
 3 import utility.SnowFlakeIdGenerator;
 4 
 5 public class SnowFlakeTest {
 6 
 7     /***
 8      * 测试Snowflake
 9      * @param args
10      */
11     public static void main(String[] args) {
12         SnowFlakeIdGenerator snowFlaker = new SnowFlakeIdGenerator(22, 11);
13         for(int i = 0; i < 100000; i++) {
14             System.out.println(Long.toBinaryString(snowFlaker.getNextId()));
15         }
16     }
17 }

 

结果:
1010110101101110001011111011001011100001110101110110101101000011
1010110101101110001011111011001011100001110101110110101101000100
1010110101101110001011111011001011100001110101110110101101000101
1010110101101110001011111011001011100001110101110110101101000110
1010110101101110001011111011001011100001110101110110101101000111
1010110101101110001011111011001011100001110101110110101101001000
1010110101101110001011111011001011100001110101110110101101001001
1010110101101110001011111011001011100001110101110110101101001010
1010110101101110001011111011001011100001110101110110101101001011
1010110101101110001011111011001011100001110101110110101101001100
1010110101101110001011111011001011100001110101110110101101001101
1010110101101110001011111011001011100001110101110110101101001110
1010110101101110001011111011001011100001110101110110101101001111
1010110101101110001011111011001011100001110101110110101101010000
1010110101101110001011111011001011100001110101110110101101010001
1010110101101110001011111011001011100001110101110110101101010010
1010110101101110001011111011001011100001110101110110101101010011
1010110101101110001011111011001011100001110101110110101101010100
1010110101101110001011111011001011100001110101110110101101010101
...

 

参考:分布式系统中唯一ID的生成方法