雪花算法(snowflake)

1、 背景
分布式系统中,有一些需要使用全局唯一ID的场景,这种时候为了防止ID冲突可以使用36位的UUID,但是UUID有一些缺点,首先他相对比较长,另外UUID一般是无序的。

有些时候我们希望能使用一种简单一些的ID,并且希望ID能够按照时间有序生成。

而twitter的snowflake解决了这种需求,最初Twitter把存储系统从MySQL迁移到Cassandra,因为Cassandra没有顺序ID生成机制,为了满足Twitter每秒上万条消息的请求,每条消息都必须分配一条唯一的id,这些id还需要一些大致的顺序(方便客户端排序),并且在分布式系统中不同机器产生的id必须不同,所以twitter开发了这样一套全局唯一ID生成服务。


2、Snowflake算法核心

  • SnowFlake的结构如下(每部分用-分开): 把时间戳,工作机器id,序列号组合在一起。
  • 雪花算法(snowflake)_第1张图片
  •  

*1位标识,由于long基本类型在Java中是带符号的,最高位是符号位,正数是0,负数是1,所以id一般是正数,最高位是0

*41位时间截(毫秒级),注意,41位时间截不是存储当前时间的时间截,而是存储时间截的差值(当前时间截 - 开始时间截) 后得到的值,这里的的开始时间截,一般是我们的id生成器开始使用的时间,由我们程序来指定的(如下下面程序IdWorker类的startTime属性)。41位的时间截,可以使用69年,年T = (1L << 41) / (1000L * 60 * 60 * 24 * 365) = 69

10位的数据机器位,可以部署在1024个节点,包括10位workerId

12位序列,毫秒内的计数,12位的计数顺序号支持每个节点每毫秒(同一机器,同一时间截)产生4096个ID序号

加起来刚好64位,为一个Long型。

 

 

public class SnowFlakeGenerator {

	public static class Factory {
		/**
		 * 每一部分占用位数的默认值
		 */
		private final static int DEFAULT_MACHINE_BIT_NUM = 5; // 机器标识占用的位数
		private final static int DEFAULT_IDC_BIT_NUM = 5;// 数据中心占用的位数

		private int machineBitNum;
		private int idcBitNum;

		public Factory() {
			this.idcBitNum = DEFAULT_IDC_BIT_NUM;
			this.machineBitNum = DEFAULT_MACHINE_BIT_NUM;
		}

		public Factory(int machineBitNum, int idcBitNum) {
			this.idcBitNum = idcBitNum;
			this.machineBitNum = machineBitNum;
		}

		public SnowFlakeGenerator create(long idcId, long machineId) {
			return new SnowFlakeGenerator(this.idcBitNum, this.machineBitNum, idcId, machineId);
		}
	}

	/**
	 * 起始的时间戳 作者写代码时的时间戳
	 */
	private final static long START_STAMP = 1553153540626L;

	/**
	 * 可分配的位数
	 */
	private final static int REMAIN_BIT_NUM = 12;

	/**
	 * idc编号
	 */
	private long idcId;

	/**
	 * 机器编号
	 */
	private long machineId;

	/**
	 * 当前序列号
	 */
	private long sequence = 0L;

	/**
	 * 上次最新时间戳
	 */
	private long lastStamp = -1L;

	/**
	 * idc偏移量:一次计算出,避免重复计算
	 */
	private int idcBitLeftOffset;

	/**
	 * 机器id偏移量:一次计算出,避免重复计算
	 */
	private int machineBitLeftOffset;

	/**
	 * 时间戳偏移量:一次计算出,避免重复计算
	 */
	private int timestampBitLeftOffset;

	/**
	 * 最大序列值:一次计算出,避免重复计算
	 */
	private int maxSequenceValue;

	private SnowFlakeGenerator(int idcBitNum, int machineBitNum, long idcId, long machineId) {
		int sequenceBitNum = REMAIN_BIT_NUM - idcBitNum - machineBitNum;

		if (idcBitNum <= 0 || machineBitNum <= 0 || sequenceBitNum <= 0) {
			throw new IllegalArgumentException("error bit number");
		}

		this.maxSequenceValue = ~(-1 << sequenceBitNum);

		machineBitLeftOffset = sequenceBitNum;
		idcBitLeftOffset = idcBitNum + sequenceBitNum;
		timestampBitLeftOffset = idcBitNum + machineBitNum + sequenceBitNum;

		this.idcId = idcId;
		this.machineId = machineId;
	}

	/**
	 * 产生下一个ID
	 */
	public synchronized long nextId() {
		long currentStamp = getTimeMill();
		if (currentStamp < lastStamp) {
			throw new RuntimeException(String.format(
					"Clock moved backwards. Refusing to generate id for %d milliseconds", lastStamp - currentStamp));
		}

		// 新的毫秒,序列从0开始,否则序列自增
		if (currentStamp == lastStamp) {
			sequence = (sequence + 1) & this.maxSequenceValue;
			if (sequence == 0L) {
				// Twitter源代码中的逻辑是循环,直到下一个毫秒
				lastStamp = tilNextMillis();
//                throw new IllegalStateException("sequence over flow");
			}
		} else {
			sequence = 0L;
		}

		lastStamp = currentStamp;

		return (currentStamp - START_STAMP) << timestampBitLeftOffset | idcId << idcBitLeftOffset
				| machineId << machineBitLeftOffset | sequence;
	}

	private long getTimeMill() {
		return System.currentTimeMillis();
	}

	private long tilNextMillis() {
		long timestamp = getTimeMill();
		while (timestamp <= lastStamp) {
			timestamp = getTimeMill();
		}
		return timestamp;
	}

	public static void main(String[] args) {
		SnowFlakeGenerator snowFlakeGenerator = new SnowFlakeGenerator.Factory().create(1, 1);
		
		long start = System.currentTimeMillis();
		for (long i = 0; i < 100000; i++) {
			System.out.println(snowFlakeGenerator.nextId());
		}
		System.out.println(System.currentTimeMillis() - start);
	}
}

 

 

 

 

/**
 * Twitter的分布式自增ID雪花算法snowflake
 * 
 *
 */
public class SnowFlake {

	/**
	 * 起始的时间戳
	 */
	private final static long START_STMP = 1553153540626L;

	/**
	 * 每一部分占用的位数
	 */
	private final static long SEQUENCE_BIT = 2; // 序列号占用的位数
	private final static long MACHINE_BIT = 5; // 机器标识占用的位数
	private final static long DATACENTER_BIT = 5;// 数据中心占用的位数

	/**
	 * 每一部分的最大值
	 */
	private final static long MAX_DATACENTER_NUM = -1L ^ (-1L << DATACENTER_BIT);
	private final static long MAX_MACHINE_NUM = -1L ^ (-1L << MACHINE_BIT);
	private final static long MAX_SEQUENCE = -1L ^ (-1L << SEQUENCE_BIT);

	/**
	 * 每一部分向左的位移
	 */
	private final static long MACHINE_LEFT = SEQUENCE_BIT;
	private final static long DATACENTER_LEFT = SEQUENCE_BIT + MACHINE_BIT;
	private final static long TIMESTMP_LEFT = DATACENTER_LEFT + DATACENTER_BIT;

	private long datacenterId; // 数据中心
	private long machineId; // 机器标识
	private long sequence = 0L; // 序列号
	private long lastStmp = -1L;// 上一次时间戳

	public SnowFlake(long datacenterId, long machineId) {
		if (datacenterId > MAX_DATACENTER_NUM || datacenterId < 0) {
			throw new IllegalArgumentException("datacenterId can't be greater than MAX_DATACENTER_NUM or less than 0");
		}
		if (machineId > MAX_MACHINE_NUM || machineId < 0) {
			throw new IllegalArgumentException("machineId can't be greater than MAX_MACHINE_NUM or less than 0");
		}
		this.datacenterId = datacenterId;
		this.machineId = machineId;
	}

	/**
	 * 产生下一个ID
	 *
	 * @return
	 */
	public synchronized long nextId() {
		long currStmp = getNowMill();
		if (currStmp < lastStmp) {
			throw new RuntimeException("Clock moved backwards.  Refusing to generate id");
		}

		if (currStmp == lastStmp) {
			// 相同毫秒内,序列号自增
			sequence = (sequence + 1) & 3;
			// 同一毫秒的序列数已经达到最大
			if (sequence == 0L) {
				currStmp = getNextMill();
			}
		} else {
			// 不同毫秒内,序列号置为0
			sequence = 0L;
		}
		lastStmp = currStmp;

		return (currStmp - START_STMP) << TIMESTMP_LEFT // 时间戳部分
				| datacenterId << DATACENTER_LEFT // 数据中心部分
				| machineId << MACHINE_LEFT // 机器标识部分
				| sequence; // 序列号部分
	}

	private long getNextMill() {
		long mill = getNowMill();
		while (mill <= lastStmp) {
			mill = getNowMill();
		}
		return mill;
	}

	private long getNowMill() {
		return System.currentTimeMillis();
	}

	public static void main(String[] args) {
		SnowFlake snowFlake = new SnowFlake(1, 1);
		long start = System.currentTimeMillis();

		for (int i = 0; i < 100000; i++) {
			System.out.println(snowFlake.nextId());
		}

		System.out.println(System.currentTimeMillis() - start);

	}
}

 

你可能感兴趣的:(java)