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