SnowFlake算法生成id的结果是一个64bit大小的整数,它的结构如下图:
雪花生成的id有64位,其中第1位是符号位,第2至42位是41位的时间戳,第42位至52位是10位的工具机器,包括5位数据中心和5位机器id,第53位到64位是12位的序列号。
SnowFlake可以保证:
● 所有生成的id按时间趋势递增
● 整个分布式系统内不会产生重复id(因为有datacenterId和workerId来做区分)
具体实现的代码如下:
package org.xiaov.bean;
import org.xiaov.bean.date.DatePattern;
/**
* <p>
*
* </p>
*
* @author xiaovcloud
* @since 2021/7/17 15:20
*/
public class Snowflake {
/**
* 起始的时间戳 2021/01/01
*/
private final static long START_STMP = DatePattern.START_STMP;
/**
* 每一部分占用的位数
*/
private final static long MACHINE_BIT = 5; //机器标识占用的位数
private final static long DATACENTER_BIT = 5;//数据中心占用的位数
private final static long SEQUENCE_BIT = 12; //序列号占用的位数
/**
* 每一部分的最大值
*/
private final static long MAX_DATACENTER_NUM = ~(-1L << DATACENTER_BIT);
private final static long MAX_MACHINE_NUM = ~(-1L << MACHINE_BIT);
private final static long MAX_SEQUENCE = ~(-1L << SEQUENCE_BIT);
/**
* 每一部分向左的位移
*/
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 final long datacenterId; //数据中心
private final long machineId; //机器标识
private long sequence = 0L; //序列号
private long lastStmp = -1L;//上一次时间戳
public Snowflake(long datacenterId, long machineId) {
if (datacenterId > MAX_DATACENTER_NUM || datacenterId < 0) {
throw new IllegalArgumentException("数据中心的id不能小于0或者大于最大值" + MAX_DATACENTER_NUM);
}
if (machineId > MAX_MACHINE_NUM || machineId < 0) {
throw new IllegalArgumentException("机器标识的id不能小于0或者大于最大值" + MAX_MACHINE_NUM);
}
this.datacenterId = datacenterId;
this.machineId = machineId;
}
/**
* 产生下一个ID
*
* @return id
*/
public synchronized long nextId() {
long currStmp = System.currentTimeMillis();
if (currStmp < lastStmp) {
throw new RuntimeException("时间改变,不能再次生成id");
}
if (currStmp == lastStmp) {
//相同毫秒内,序列号自增
sequence = (sequence + 1) & MAX_SEQUENCE;
//同一毫秒的序列数已经达到最大
if (sequence == 0L) {
currStmp = getNextMill();
}
} else {
//不同毫秒内,序列号置为0
sequence = 0L;
}
lastStmp = currStmp;
return (currStmp - START_STMP) << TIMESTMP_LEFT //时间戳部分
| datacenterId << DATACENTER_LEFT //数据中心部分
| machineId << MACHINE_LEFT //机器标识部分
| sequence; //序列号部分
}
/**
* 获取下一毫秒
*
* @return 。
*/
private long getNextMill() {
long mill = System.currentTimeMillis();
while (mill <= lastStmp) {
mill = System.currentTimeMillis();
}
return mill;
}
}
package org.xiaov.extra.id;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.stereotype.Component;
import org.xiaov.bean.Snowflake;
import org.xiaov.bean.date.DatePattern;
import java.util.Date;
/**
* <p>
* 雪花算法生成id帮助类
* </p>
*
* @author xiaovcloud
* @since 2021/7/8 16:42
*/
@Component
public class SnowflakeHelper {
/**
* 起始时间
*/
private static final long START_STMP = DatePattern.START_STMP;
/**
* 机器码
*/
private static long machineId;
/**
* 数据中心
*/
private static long datacenterId;
@Value("${id.snowflake.machineId:1}")
public void setMachineId(long machine) {
machineId = machine;
}
@Value("${id.snowflake.datacenterId:1}")
public void setDatacenterId(long datacenter) {
datacenterId = datacenter;
}
/**
* 获取id
*
* @return id
*/
public static long nextId() {
Snowflake snowflake = new Snowflake(datacenterId, machineId);
return snowflake.nextId();
}
/**
* 根据雪花id 反推时间
*
* @param id 雪花id
* @return 时间
*/
public static Date getDateById(long id) {
long timeStamp = getTimestampById(id);
return new Date(timeStamp);
}
/**
* 根据雪花id 反推时间戳
*
* @param id 雪花id
* @return 时间戳
*/
public static long getTimestampById(long id) {
long timeStamp = id >> 22;
timeStamp = timeStamp + START_STMP;
return timeStamp;
}
}