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