snowflake分布式ID计算
2021-08-19 本文已影响0人
SparkOnly
**
* 分布式ID生成算法
* 毫秒数在高位,自增序列在低位,整个ID都是趋势递增的
41位的时间截,可以使用69年,年T = (1L << 41) / (1000L * 60 * 60 * 24 * 365) = 69
* 优点:1. 生成id不依赖db,完全内存生成,高性能高可用; 2. id呈趋势递增,后续插入索引树的时候性能好
* 缺点:如果某台机器的系统时钟回拨,有可能造成id冲突,或者id乱序
*/
public class SnowFlake {
//工作节点ID(0~31)
private long workerId;
//数据中心ID(0~31)
private long datacenterId;
//毫秒内序列(0~4095)
private long sequence = 0L;
//初始时间戳 2010-11-04T09:42:54.657到1970-01-01T00:00:00.000时刻所经过的毫秒数。
//如果不减去这个值,就浪费了40年的时间戳
private long twepoch = 1288834974657L;
//机器id所占位数
private long workerIdBits = 5L;
//数据中心id所占位数
private long datacenterIdBits = 5L;
//最大支持机器id 2^5-1=31
private long maxWorkerId = -1L^(-1L << workerIdBits);
//最大支持数据中心id 2^5-1=31
private long maxDatacenterId = -1L ^ (-1L << datacenterIdBits);
//序列号所占位数
private long sequenceBits = 12L;
//机器偏移量(向左移12位)
private long workerIdShift = sequenceBits;
//数据中心偏移量(向左移12+5=17位)
private long datacenterIdShift = sequenceBits + workerIdBits;
//时间戳偏移量(向左移12+5+5=22位),本身占42位
private long timestampLeftShift = sequenceBits + workerIdBits + datacenterIdBits;
//生成序列的掩码 4095 0b111111111111=0xfff=4095
// 使用mask可以防止溢出
private long sequenceMask = -1L ^ (-1L << sequenceBits);
private long lastTimestamp = -1L;
public SnowFlake(long workerId, long datacenterId, long sequence){
if(workerId > maxWorkerId || workerId < 0){
throw new IllegalArgumentException(String.format("worker Id can't be greater than %d or less than 0", maxWorkerId));
}
if(datacenterId > maxDatacenterId || datacenterId < 0){
throw new IllegalArgumentException(String.format("datacenterId Id can't be greater than %d or less than 0", maxWorkerId));
}
System.out.printf("worker starting. timestamp left shift %d, datacenter id bits %d, worker id bits %d, sequence bits %d, workerid %d",
timestampLeftShift, datacenterIdBits, workerIdBits, sequenceBits, workerId);
this.workerId = workerId;
this.datacenterId = datacenterId;
this.sequence = sequence;
}
//使用synchronized,而不是CAS,是因为CAS不适合并发量非常高的情况
public synchronized long nextId(){
long timestamp = timeGen();
//如果时间回拨了,可能id会重复
if(timestamp < lastTimestamp){
System.err.printf("clock is moving backwards. Rejecting requests until %d.", lastTimestamp);
throw new RuntimeException(String.format("Clock moved backwards. Refusing to generate id for %d milliseconds",
lastTimestamp - timestamp));
}
if(lastTimestamp == timestamp){
sequence = (sequence + 1) & sequenceMask;
//sequence==0说明毫秒内序列已经达到最大值,阻塞等到下个序列
if(sequence == 0){
timestamp = tilNextMillis(lastTimestamp);
}
}else{
//时间戳改变时,序列重置
sequence = 0;
}
lastTimestamp = timestamp;
return ((timestamp - twepoch) << timestampLeftShift) |
(datacenterId << datacenterIdShift) |
(workerId << workerIdShift) |
sequence;
}
private long tilNextMillis(long lastTimestamp){
long timestamp = timeGen();
while (timestamp <= lastTimestamp){
timestamp = timeGen();
}
return timestamp;
}
private long timeGen(){
return System.currentTimeMillis();
}
public static void main(String[] args) {
SnowFlake test = new SnowFlake(1, 1, 1);
for(int i=0;i<10;i++){
long x = test.nextId();
System.out.println(x + "\t" + Long.toBinaryString(x));
}
}