雪花算法的原理

目录

前提

原理

代码

深度解析

4096

​ 69

1024

 总结


前提

需要对位运算符有所了解,运算符之位运算符

原理

1,如图所示,整个id的生成,是有三部分组合在一起的

2,第一部分是时间,站位41bit;第二部分是工作机器id,站位10bit;第三部分是序列号,站位12bit。

3,第一部分时间的生成规则是:当前时间戳【System.currentTimeMillis()】,减去初始时间【private long twepoch = 1628850054648L;】,会得到一个时间差,第一位存放的就是这个时间差。

4,第二部分工作机器id规则:有数据中心(5bit)+机器节点(5bit)组合在一起。可以满足多钟场景使用。

5,第三部分序列号规则:一个由0不断自增的整数。当处于高并发的情况下,每毫秒都需要生成大量的id时,只靠时间差来保证唯一性,已经不能满足需求了。序列号,不断自增,由序列表+时间差就能保证唯一性了。

6,核心:把如上三部分,根据前后顺序,拼接在一起,唯一id就出来了。拼接主要是利用位运算符,来实现的。

代码

/**
 * 生成id
 * 每毫秒可产生4096不同id
 * 最多可以使用69.73年
 * 
 * @author frank—fu
 *
 */
public class IdGen {
	//机器节点
	private long workerId;
	//数据中心
	private long datacenterId;
	//序列号(自增)
	private long sequence = 0L;
	//初始时间(Thu, 04 Nov 2010 01:42:54 GMT)
	//主要是用来计算时间差(当前时间-twepoch)
	//这个时间最好是系统第一次上线时间,这样可以使用大概69.73年
	private long twepoch = 1628850054648L;
	//机器节点ID长度
	private long workerIdBits = 5L; 
	//数据中心ID长度
	private long datacenterIdBits = 5L;
	//最大支持机器节点数0~31,一共32个
	private long maxWorkerId = -1L ^ (-1L << workerIdBits);
	//最大支持数据中心节点数0~31,一共32个
	private long maxDatacenterId = -1L ^ (-1L << datacenterIdBits); 
	//序列号长度
	private long sequenceBits = 12L;
	//机器节点需要左移位数(sequenceBits = 12)
	private long workerIdShift = sequenceBits;
	//数据中心节点需要左移位数(sequenceBits + workerIdBits = 17)
	private long datacenterIdShift = sequenceBits + workerIdBits;
	//时间毫秒数需要左移位数(sequenceBits + workerIdBits + datacenterIdBits = 22)
	private long timestampLeftShift = sequenceBits + workerIdBits + datacenterIdBits;
	//sequence的最大值4095,如果为4095,则sequence=0
	private long sequenceMask = -1L ^ (-1L << sequenceBits);
	//上次获取id的时间,默认为-1
	private long lastTimestamp = -1L;

	private static class IdGenHolder {
		private static final IdGen instance = new IdGen();
	}

	public static IdGen getInStance() {
		return IdGenHolder.instance;
	}

	public IdGen() {
		this(0L, 0L);
	}

	public IdGen(long workerId, long datacenterId) {
		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("datacenter Id can't be greater than %d or less than 0", maxDatacenterId));
		}
		this.workerId = workerId;
		this.datacenterId = datacenterId;
	}

	public synchronized long nextId() {
		//获取当前毫秒数
		long timestamp = timeGen(); 
		//如果服务器时间有问题(时钟后退) 报错。
		if (timestamp < lastTimestamp) {
			throw new RuntimeException(String.format(
					"Clock moved backwards.  Refusing to generate id for %d milliseconds", lastTimestamp - timestamp));
		}
		//如果上次生成时间和当前时间相同,在同一毫秒内
		if (lastTimestamp == timestamp) {
			//sequence自增,因为sequence只有12bit,所以和sequenceMask相与一下,去掉高位
			sequence = (sequence + 1) & sequenceMask;
			//判断是否溢出,也就是每毫秒内超过4095,当为4096时,与sequenceMask相与,sequence就等于0
			if (sequence == 0) {
				timestamp = tilNextMillis(lastTimestamp); //自旋等待到下一毫秒
			}
		} else {
			//如果和上次生成时间不同,重置sequence,就是下一毫秒开始,sequence计数重新从0开始累加
			sequence = 0L;
		}
		//保存本次生成id的时间
		lastTimestamp = timestamp;
		//核心计算
		//1,(timestamp - twepoch) << timestampLeftShift (当前时间减去初始时间)的值左移22位
		//2,datacenterId << datacenterIdShift 数据中心左移17位
		//3,workerId << workerIdShift 机器节点左移12位
		//利用|的特性,把数据拼接起来
		return ((timestamp - twepoch) << timestampLeftShift) | (datacenterId << datacenterIdShift)
				| (workerId << workerIdShift) | sequence;
	}

	protected long tilNextMillis(long lastTimestamp) {
		long timestamp = timeGen();
		while (timestamp <= lastTimestamp) {
			timestamp = timeGen();
		}
		return timestamp;
	}

	protected long timeGen() {
		return System.currentTimeMillis();
	}

}

深度解析

1,为什么1毫秒最多可以产生4096个id?

2,为什么最好只能使用69年?

3,为什么最多支持1024个机器节点?

4096

序列号是12bit组成:1111 1111 1111 转成二进制 4095

 69

整个函数return的是long【public synchronized long nextId()】;long 占64bit,64-1(占位符)-10(机器)-12(序列号)= 41 bit;也就是说时间差最多只能占41bit

转成十进制,也就是2199023255551,时间差最大的值就是2199023255551毫秒。69年=2,175,984,000,000毫秒

1024

机器占10bit(数据中心(5bit)+机器节点(5bit)),1111111111 转成十进制是1023,加上0,就是1024

 总结

1,1毫秒最多产生4096个不同的id

2,最多可使用69年

3,最多可以支持1024个机器 

  • 0
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值