加入收藏 | 设为首页 | 会员中心 | 我要投稿 李大同 (https://www.lidatong.com.cn/)- 科技、建站、经验、云计算、5G、大数据,站长网!
当前位置: 首页 > 大数据 > 正文

全局唯一ID生成器(Snowflake ID组成) 分析

发布时间:2020-12-14 04:44:29 所属栏目:大数据 来源:网络整理
导读:? Snowflake?ID组成 Snowflake?ID有64bits长,由以下三部分组成: time—42bits,精确到ms,那就意味着其可以表示长达(2^42-1)/(1000 3600 24*365)=139.5年,另外使用者可以自己定义一个开始纪元(epoch),然后用(当前时间-开始纪元)算出time,这表示在time

?

Snowflake?ID组成

Snowflake?ID有64bits长,由以下三部分组成:

  • time—42bits,精确到ms,那就意味着其可以表示长达(2^42-1)/(1000360024*365)=139.5年,另外使用者可以自己定义一个开始纪元(epoch),然后用(当前时间-开始纪元)算出time,这表示在time这个部分在140年的时间里是不会重复的,官方文档在这里写成了41bits,应该是写错了。另外,这里用time还有一个很重要的原因,就是可以直接更具time进行排序,对于twitter这种更新频繁的应用,时间排序就显得尤为重要了。

  • machine?id—10bits,该部分其实由datacenterId和workerId两部分组成,这两部分是在配置文件中指明的。

    • datacenterId的作用(个人看法)

      1.方便搭建多个生成uid的service,并保证uid不重复,比如在datacenter0将机器0,1,2组成了一个生成uid的service,而datacenter1此时也需要一个生成uid的service,从本中心获取uid显然是最快最方便的,那么它可以在自己中心搭建,只要保证datacenterId唯一。如果没有datacenterId,即用10bits,那么在搭建一个新的service前必须知道目前已经在用的id,否则不能保证生成的id唯一,比如搭建的两个uid?service中都有machine?id为100的机器,如果其server时间相同,那么产生相同id的情况不可避免。

      2.加快server启动速度。启动一台uid?server时,会去检查zk同workerId目录中其他机器的情况,如其在zk上注册的id和向它请求返回的work_id是否相同,是否处同一个datacenter下,另外还会检查该server的时间与目前已有机器的平均时间误差是否在10s范围内等,这些检查是会耗费一定时间的。将一个datacenter下的机器数限制在32台(5bits)以内,在一定程度上也保证了server的启动速度。

    • workerId是实际server机器的代号,最大到32,同一个datacenter下的workerId是不能重复的。它会被注册到zookeeper上,确保workerId未被其他机器占用,并将host:port值存入,注册成功后就可以对外提供服务了。

  • sequence?id?—12bits,该id可以表示4096个数字,它是在time相同的情况下,递增该值直到为0,即一个循环结束,此时便只能等到下一个ms到来,一般情况下4096/ms的请求是不太可能出现的,所以足够使用了。

Snowflake?ID便是通过这三部分实现了UID的产生,策略也并不复杂。下面我们来看看它的一些关键源码

?

/**
* 核心代码就是毫秒级时间41位+机器ID 10位+毫秒内序列12位
* */

public class IdWorker {


private long workerId;?
private long datacenterId;?
private long sequence = 0L;?

private long twepoch = 1288834974657L;?

private long workerIdBits = 5L;?
private long datacenterIdBits = 5L;?
private long maxWorkerId = -1L ^ (-1L << workerIdBits);?
private long maxDatacenterId = -1L ^ (-1L << datacenterIdBits);?
private long sequenceBits = 12L;?

private long workerIdShift = sequenceBits;?
private long datacenterIdShift = sequenceBits + workerIdBits;?
private long timestampLeftShift = sequenceBits + workerIdBits + datacenterIdBits;?
private long sequenceMask = -1L ^ (-1L << sequenceBits);?

private long lastTimestamp = -1L;?

public IdWorker(long workerId,long datacenterId) {?
// sanity check for workerId?
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) {?
System.out.println("clock is moving backwards. Rejecting requests until "+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;?
if (sequence == 0) {?
timestamp = tilNextMillis(lastTimestamp);?
}?
} else {?
sequence = 0L;?
}?

lastTimestamp = timestamp;?

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();?
}?
}

?

?

原文:https://www.cnblogs.com/dayhand/p/3851248.html

(编辑:李大同)

【声明】本站内容均来自网络,其相关言论仅代表作者个人观点,不代表本站立场。若无意侵犯到您的权利,请及时与联系站长删除相关内容!

    推荐文章
      热点阅读