前言
項目中主鍵ID生成方式比較多,但是哪種方式更能提高的我們的工作效率、項目質量、代碼實用性以及健壯性呢,下面作了一下比較,目前雪花算法的優點還是很明顯的。
優缺點比較
- UUID(缺點:太長、沒法排序、使數據庫性能降低)
- Redis(缺點:必須依賴Redis)
- Oracle序列號(缺點:用Oracle才能使用)
- Snowflake雪花算法,優點:生成有順序的id,提高數據庫的性能
Snowflake雪花算法解析

雪花算法解析 結構 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型。(轉換成字符串長度為18)。
Snowflake算法核心把時間戳,工作機器id,序列號組合在一起。
整體上按照時間自增排序,並且整個分布式系統內不會產生ID碰撞(由datacenter和機器ID作區分),
並且效率較高,經測試,snowflake每秒能夠產生26萬ID左右,完全滿足需要。
分布式Snowflake雪花算法代碼
1 public class SnowFlakeGenerator { 2 3 public static class Factory { 4 /** 5 * 每一部分占用位數的默認值 6 */ 7 private final static int DEFAULT_MACHINE_BIT_NUM = 5; //機器標識占用的位數 8 private final static int DEFAULT_IDC_BIT_NUM = 5;//數據中心占用的位數 9 10 private int machineBitNum; 11 private int idcBitNum; 12 13 public Factory() { 14 this.idcBitNum = DEFAULT_IDC_BIT_NUM; 15 this.machineBitNum = DEFAULT_MACHINE_BIT_NUM; 16 } 17 18 public Factory(int machineBitNum, int idcBitNum) { 19 this.idcBitNum = idcBitNum; 20 this.machineBitNum = machineBitNum; 21 } 22 23 public SnowFlakeGenerator create(long idcId, long machineId) { 24 return new SnowFlakeGenerator(this.idcBitNum, this.machineBitNum, idcId, machineId); 25 } 26 } 27 28 /** 29 * 起始的時間戳 30 * 作者寫代碼時的時間戳 31 */ 32 private final static long START_STAMP = 1508143349995L; 33 34 /** 35 * 可分配的位數 36 */ 37 private final static int REMAIN_BIT_NUM = 22; 38 39 /** 40 * idc編號 41 */ 42 private long idcId; 43 44 /** 45 * 機器編號 46 */ 47 private long machineId; 48 49 /** 50 * 當前序列號 51 */ 52 private long sequence = 0L; 53 54 /** 55 * 上次最新時間戳 56 */ 57 private long lastStamp = -1L; 58 59 /** 60 * idc偏移量:一次計算出,避免重復計算 61 */ 62 private int idcBitLeftOffset; 63 64 /** 65 * 機器id偏移量:一次計算出,避免重復計算 66 */ 67 private int machineBitLeftOffset; 68 69 /** 70 * 時間戳偏移量:一次計算出,避免重復計算 71 */ 72 private int timestampBitLeftOffset; 73 74 /** 75 * 最大序列值:一次計算出,避免重復計算 76 */ 77 private int maxSequenceValue; 78 79 private SnowFlakeGenerator(int idcBitNum, int machineBitNum, long idcId, long machineId) { 80 int sequenceBitNum = REMAIN_BIT_NUM - idcBitNum - machineBitNum; 81 82 if (idcBitNum <= 0 || machineBitNum <= 0 || sequenceBitNum <= 0) { 83 throw new IllegalArgumentException("error bit number"); 84 } 85 86 this.maxSequenceValue = ~(-1 << sequenceBitNum); 87 88 machineBitLeftOffset = sequenceBitNum; 89 idcBitLeftOffset = idcBitNum + sequenceBitNum; 90 timestampBitLeftOffset = idcBitNum + machineBitNum + sequenceBitNum; 91 92 this.idcId = idcId; 93 this.machineId = machineId; 94 } 95 96 /** 97 * 產生下一個ID 98 */ 99 public synchronized long nextId() { 100 long currentStamp = getTimeMill(); 101 if (currentStamp < lastStamp) { 102 throw new RuntimeException(String.format("Clock moved backwards. Refusing to generate id for %d milliseconds", lastStamp - currentStamp)); 103 } 104 105 //新的毫秒,序列從0開始,否則序列自增 106 if (currentStamp == lastStamp) { 107 sequence = (sequence + 1) & this.maxSequenceValue; 108 if (sequence == 0L) { 109 //Twitter源代碼中的邏輯是循環,直到下一個毫秒 110 lastStamp = tilNextMillis(); 111 // throw new IllegalStateException("sequence over flow"); 112 } 113 } else { 114 sequence = 0L; 115 } 116 117 lastStamp = currentStamp; 118 119 return (currentStamp - START_STAMP) << timestampBitLeftOffset | idcId << idcBitLeftOffset | machineId << machineBitLeftOffset | sequence; 120 } 121 122 private long getTimeMill() { 123 return System.currentTimeMillis(); 124 } 125 126 private long tilNextMillis() { 127 long timestamp = getTimeMill(); 128 while (timestamp <= lastStamp) { 129 timestamp = getTimeMill(); 130 } 131 return timestamp; 132 } 133 }
