前言

项目中主键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 }

 

posted on 2018-11-29 14:57  风又奈何  阅读(11040)  评论(1编辑  收藏  举报