系統唯一ID是我們在設計一個系統的時候常常會遇見的問題,也常常為這個問題而糾結。生成ID的方法有很多,適應不同的場景、需求以及性能要求。所以有些比較復雜的系統會有多個ID生成的策略。下面就介紹一些常見的ID生成策略。
平常應用中方式很多
1. 數據庫自增長序列或字段
2. UUID
3.uuid的變種
4.redis生成
5. Twitter的snowflake算法
snowflake是Twitter開源的分布式ID生成算法,結果是一個long型的ID。其核心思想是:使用41bit作為毫秒數,10bit作為機器的ID(5個bit是數據中心,5個bit的機器ID),12bit作為毫秒內的流水號(意味着每個節點在每毫秒可以產生 4096 個 ID),最后還有一個符號位,永遠是0。
本文主要是詳解snowflake算法的實現代碼解析
1 /** 2 * Twitter_Snowflake<br> 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類的 8 startTime屬性)。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作區分),並且效率較高,經測試,SnowFlake每秒能夠產生26萬ID左右。 13 */ 14 public class SnowflakeIdWorker { 15 16 // ==============================Fields=========================================== 17 /** 開始時間截 (2015-01-01) */ 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 //==============================Constructors===================================== 60 /** 61 * 構造函數 62 * @param workerId 工作ID (0~31) 63 * @param datacenterId 數據中心ID (0~31) 64 */ 65 public SnowflakeIdWorker(long workerId, long datacenterId) { 66 if (workerId > maxWorkerId || workerId < 0) { 67 throw new IllegalArgumentException(String.format("worker Id can't be greater than %d or less than 0", maxWorkerId)); 68 } 69 if (datacenterId > maxDatacenterId || datacenterId < 0) { 70 throw new IllegalArgumentException(String.format("datacenter Id can't be greater than %d or less than 0", maxDatacenterId)); 71 } 72 this.workerId = workerId; 73 this.datacenterId = datacenterId; 74 } 75 76 // ==============================Methods========================================== 77 /** 78 * 獲得下一個ID (該方法是線程安全的) 79 * @return SnowflakeId 80 */ 81 public synchronized long nextId() { 82 long timestamp = timeGen(); 83 84 //如果當前時間小於上一次ID生成的時間戳,說明系統時鍾回退過這個時候應當拋出異常 85 if (timestamp < lastTimestamp) { 86 throw new RuntimeException( 87 String.format("Clock moved backwards. Refusing to generate id for %d milliseconds", lastTimestamp - timestamp)); 88 } 89 90 //如果是同一時間生成的,則進行毫秒內序列 91 if (lastTimestamp == timestamp) { 92 sequence = (sequence + 1) & sequenceMask; 93 //毫秒內序列溢出 94 if (sequence == 0) { 95 //阻塞到下一個毫秒,獲得新的時間戳 96 timestamp = tilNextMillis(lastTimestamp); 97 } 98 } 99 //時間戳改變,毫秒內序列重置 100 else { 101 sequence = 0L; 102 } 103 104 //上次生成ID的時間截 105 lastTimestamp = timestamp; 106 107 //移位並通過或運算拼到一起組成64位的ID 108 return ((timestamp - twepoch) << timestampLeftShift) // 109 | (datacenterId << datacenterIdShift) // 110 | (workerId << workerIdShift) // 111 | sequence; 112 } 113 114 /** 115 * 阻塞到下一個毫秒,直到獲得新的時間戳 116 * @param lastTimestamp 上次生成ID的時間截 117 * @return 當前時間戳 118 */ 119 protected long tilNextMillis(long lastTimestamp) { 120 long timestamp = timeGen(); 121 while (timestamp <= lastTimestamp) { 122 timestamp = timeGen(); 123 } 124 return timestamp; 125 } 126 127 /** 128 * 返回以毫秒為單位的當前時間 129 * @return 當前時間(毫秒) 130 */ 131 protected long timeGen() { 132 return System.currentTimeMillis(); 133 } 134 135 //==============================Test============================================= 136 /** 測試 */ 137 public static void main(String[] args) { 138 // System.out.println(Long.toBinaryString(5)); 139 SnowflakeIdWorker idWorker = new SnowflakeIdWorker(1, 1); 140 for (int i = 0; i < 1000; i++) { 141 long id = idWorker.nextId(); 142 System.out.println(Long.toBinaryString(id)); 143 System.out.println(id); 144 } 145 } 146 }
附帶有直接的測試方法,建議動手可以敲一下看一下其中的原理。
如有問題或者錯誤煩請指出!