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

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的产生,策略也并不复杂。下面我们来看看它的一些关键源码

扫描二维码关注公众号,回复: 6429758 查看本文章

/**
* 核心代码就是毫秒级时间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

猜你喜欢

转载自www.cnblogs.com/panchanggui/p/11008439.html