SnowFlake算法Twitter提出的一种算法,如果是MySQL数据库的主键采用BIGINT的话,那么他的取值范围是-2^63 到 2^63 ,即存储一个BIGINT类型需要64位二进制。雪花算法就是针对这64位进行设计。
第1位二进制值固定位0,没有业务含义。第2~42位,共41位二进制,为时间戳,用于存入精确到毫秒数的时间。第43~52位,共10位二进制,为工作机器id位。第53~64位,共12位二进制,代表1ms内可以产生的序列号,当它表示整数时,取值区间为[0,4095]
这样的话,1ms内产生4096个编号,1秒就最多产生4096000个ID,这样的性能就能满足分布式系统的需求。
public class SnowFlakeWorker {
//开始时间(这里使用2019年4月1日整点)
private final static long START_TIME = 1554048000000L;
//数据中心编号所占数位
private final static long DATA_CENTER_BITS = 10L;
//最大数据中心编号
private final static long MAX_DATA_CENTER_ID = 1023;
//序列编号占位位数
private final static long SEQUENCE_BIT = 12L;
//数据中心编号向左移12位
private final static long DATA_CENTER_SHIFT = SEQUENCE_BIT;
//时间戳向左移22位(10+12)
private final static long TIMESTAMP_SHIFT = DATA_CENTER_BITS+DATA_CENTER_SHIFT;
//最大生成序列号,这里为4095
private final static long MAX_SEQUENCE = 4095;
//数据中心ID(0~1023)
private long dataCenterId;
//毫秒内序列(0~4095)
private long sequence = 0L;
//上次生成ID的时间戳
private long lastTimestamp = -1L;
/*
现在的微服务和分布式趋于中心化,所以不需要受理机器编号
10位二进制全部用于数据中心
*/
public SnowFlakeWorker(long dataCenterId){
if (dataCenterId>MAX_DATA_CENTER_ID){
String msg = "数据中心编号["+dataCenterId+"]超过最大允许值["+MAX_DATA_CENTER_ID+"]";
throw new RuntimeException(msg);
}
if (dataCenterId
//获取当前时间
long timestamp = System.currentTimeMillis();
//如果是同一个毫秒时间戳的处理
if(timestamp == lastTimestamp){
sequence+=1;//序号+1
if(sequence>MAX_SEQUENCE){
sequence = 0;
//等待到下一毫秒
timestamp = tilNextMillis(timestamp);
}
}else {
//修改时间戳
lastTimestamp = timestamp ;
//序列号重新开始
sequence = 0;
}
//二进制的位运算,其中“
timestamp = System.currentTimeMillis();
}while (timestamp>lastTimestamp);
return timestamp;
}
public static void main(String[] args) {
SnowFlakeWorker snowFlakeWorker = new SnowFlakeWorker(1000);
for (int i = 0;i |