一、关于雪花

雪花(snowflake)在自然界中,是极具独特美丽,又变幻莫测的东西:雪花属于六方晶系,它具有四个结晶轴,其中三个辅轴在一个基面上,互相以60度的角度相交,第四轴(主晶轴)与三个辅轴所形成的基面垂直;

雪花的基本形状是六角形,但是大自然中却几乎找不出两朵完全相同的雪花,每一个雪花都拥有自己的独有图案,就象地球上找不出两个完全相同的人一样。许多学者用显微镜观测过成千上万朵雪花,这些研究最后表明,形状、大小完全一样和各部分完全对称的雪花,在自然界中是无法形成的。

java雪花算法demo java雪花算法代码_java雪花算法demo

二、雪花算法

雪花算法的原始版本是scala版,用于生成分布式ID(纯数字,时间顺序),订单编号等。自增ID:对于数据敏感场景不宜使用,且不适合于分布式场景。GUID:采用无意义字符串,数据量增大时造成访问过慢,且不宜排序。

java雪花算法demo java雪花算法代码_序列号_02

算法描述:最高位是符号位,始终为0,不可用。

41位的时间序列,精确到毫秒级,41位的长度可以使用69年。时间位还有一个很重要的作用是可以根据时间进行排序。

10位的机器标识,10位的长度最多支持部署1024个节点。

12位的计数序列号,序列号即一系列的自增id,可以支持同一节点同一毫秒生成多个ID序号,12位的计数序列号支持每个节点每毫秒产生4096个ID序号。

三、实现过程

①、创建雪花算法实例

public class SnowFlake {
/**
* 起始的时间戳
*/
private final static long START_STMP = 1480166465631L;
/**
* 每一部分占用的位数
*/
private final static long SEQUENCE_BIT = 12; //序列号占用的位数
private final static long MACHINE_BIT = 5;  //机器标识占用的位数
private final static long DATACENTER_BIT = 5;//数据中心占用的位数
/**
* 每一部分的最大值
*/
private final static long MAX_DATACENTER_NUM = -1L ^ (-1L <
private final static long MAX_MACHINE_NUM = -1L ^ (-1L <
private final static long MAX_SEQUENCE = -1L ^ (-1L <
/**
* 每一部分向左的位移
*/
private final static long MACHINE_LEFT = SEQUENCE_BIT;
private final static long DATACENTER_LEFT = SEQUENCE_BIT + MACHINE_BIT;
private final static long TIMESTMP_LEFT = DATACENTER_LEFT + DATACENTER_BIT;
private long datacenterId;  //数据中心
private long machineId;    //机器标识
private long sequence = 0L; //序列号
private long lastStmp = -1L;//上一次时间戳
public SnowFlake(long datacenterId, long machineId) {
if (datacenterId > MAX_DATACENTER_NUM || datacenterId 
throw new IllegalArgumentException("datacenterId can't be greater than MAX_DATACENTER_NUM or less than 0");
}
if (machineId > MAX_MACHINE_NUM || machineId 
throw new IllegalArgumentException("machineId can't be greater than MAX_MACHINE_NUM or less than 0");
}
this.datacenterId = datacenterId;
this.machineId = machineId;
}
/**
* 产生下一个ID
*
* @return
*/
public synchronized long nextId() {
long currStmp = getNewstmp();
if (currStmp 
throw new RuntimeException("Clock moved backwards.  Refusing to generate id");
}
if (currStmp == lastStmp) {
//相同毫秒内,序列号自增
sequence = (sequence + 1) & MAX_SEQUENCE;
//同一毫秒的序列数已经达到最大
if (sequence == 0L) {
currStmp = getNextMill();
}
} else {
//不同毫秒内,序列号置为0
sequence = 0L;
}
lastStmp = currStmp;
return (currStmp - START_STMP) <
| datacenterId <
| machineId <
| sequence;                            //序列号部分
}
private long getNextMill() {
long mill = getNewstmp();
while (mill <= lastStmp) {
mill = getNewstmp();
}
return mill;
}
private long getNewstmp() {
return System.currentTimeMillis();
}
}
②、创建雪花算法工厂类public static class Factory {
/**
* 每一部分占用位数的默认值
*/
private final static int DEFAULT_MACHINE_BIT_NUM = 5;   //机器标识占用的位数
private final static int DEFAULT_IDC_BIT_NUM = 5;//数据中心占用的位数
private int machineBitNum;
private int idcBitNum;
public Factory() {
this.idcBitNum = DEFAULT_IDC_BIT_NUM;
this.machineBitNum = DEFAULT_MACHINE_BIT_NUM;
}
public Factory(int machineBitNum, int idcBitNum) {
this.idcBitNum = idcBitNum;
this.machineBitNum = machineBitNum;
}
public SnowFlakeGenerator create(long idcId, long machineId) {
return new SnowFlakeGenerator(this.idcBitNum, this.machineBitNum, idcId, machineId);
}
}
③、雪花算法核心代码private SnowFlakeGenerator(int idcBitNum, int machineBitNum, long idcId, long machineId) {
int sequenceBitNum = REMAIN_BIT_NUM - idcBitNum - machineBitNum;
if (idcBitNum <= 0 || machineBitNum <= 0 || sequenceBitNum <= 0) {
throw new IllegalArgumentException("error bit number");
}
this.maxSequenceValue = ~(-1 <
machineBitLeftOffset = sequenceBitNum;
idcBitLeftOffset = idcBitNum + sequenceBitNum;
timestampBitLeftOffset = idcBitNum + machineBitNum + sequenceBitNum;
this.idcId = idcId;
this.machineId = machineId;
}
/**
* 产生下一个ID
*/
public synchronized long nextId() {
long currentStamp = getTimeMill();
if (currentStamp 
throw new RuntimeException(String.format("Clock moved backwards. Refusing to generate id for %d milliseconds", lastStamp - currentStamp));
}
//新的毫秒,序列从0开始,否则序列自增
if (currentStamp == lastStamp) {
sequence = (sequence + 1) & this.maxSequenceValue;
if (sequence == 0L) {
//Twitter源代码中的逻辑是循环,直到下一个毫秒
lastStamp = tilNextMillis();
//                throw new IllegalStateException("sequence over flow");
}
} else {
sequence = 0L;
}
lastStamp = currentStamp;
return (currentStamp - START_STAMP) <
}
private long getTimeMill() {
return System.currentTimeMillis();
}
private long tilNextMillis() {
long timestamp = getTimeMill();
while (timestamp <= lastStamp) {
timestamp = getTimeMill();
}
return timestamp;
}

④、项目效果图

java雪花算法demo java雪花算法代码_自增_03

四、项目结构图

java雪花算法demo java雪花算法代码_自增_04

五、补充

雪花算法的缺点:

1.毫秒数在高位,自增序列在低位,整个ID都是趋势递增的。

2.不依赖数据库等第三方系统,以服务的方式部署,稳定性更高,生成ID的性能也是非常高的。

3.可以根据自身业务特性分配bit位,非常灵活。

雪花算法的缺点:强依赖机器时钟,如果机器上时钟回拨,会导致发号重复或者服务会处于不可用状态。