分布式Snowflake雪花算法


 前言

項目中主鍵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 }

 


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM