简介
SnowFlake 算法,是 Twitter 开源的分布式 id 生成算法。
其核心思想就是:使用一个 64 bit 的 long 型的数字作为全局唯一 id。
在分布式系统中的应用十分广泛,且ID 引入了时间戳,基本上保持自增的,后面的代码中有详细的注解。
原理
如图:
最高位是符号位,始终为0,表示正数,不可用。
41位的时间序列,精确到毫秒级,41位的长度可以使用69年。时间位还有一个很重要的作用是可以根据时间进行排序。
10位的机器标识,10位的长度最多支持部署1024个节点。
12位的计数序列号,序列号即一系列的自增id,可以支持同一节点同一毫秒生成多个ID序号,12位的计数序列号支持每个节点每毫秒产生4096个ID序号。
Java实现
import java.io.Serializable;public class Snowflake implements Serializable { private static final long serialVersionUID = 1L ; private final long twepoch = 1546272000000L ; private final long workerIdBits = 5L ; private final long datacenterIdBits = 5L ; private final long maxWorkerId = -1L ^ (-1L << workerIdBits); private final long maxDatacenterId = -1L ^ (-1L << datacenterIdBits); private final long sequenceBits = 12L ; private final long workerIdShift = sequenceBits; private final long datacenterIdShift = sequenceBits + workerIdBits; private final long timestampLeftShift = sequenceBits + workerIdBits + datacenterIdBits; private final long sequenceMask = -1L ^ (-1L << sequenceBits); private long workerId; private long dataCenterId; private long sequence = 0L ; private long lastTimestamp = -1L ; public Snowflake (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 long getWorkerId (long id) { return id >> workerIdShift & ~(-1L << workerIdBits); } public long getDataCenterId (long id) { return id >> datacenterIdShift & ~(-1L << datacenterIdBits); } public long getGenerateDateTime (long id) { return (id >> timestampLeftShift & ~(-1L << 41L )) + twepoch; } public synchronized long nextId () { long timestamp = genTime(); if (timestamp < lastTimestamp) { throw new IllegalStateException(String.format("Clock moved backwards. Refusing to generate id for %d ms" , 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; } public String nextIdStr () { return Long.toString(nextId()); } private long tilNextMillis (long lastTimestamp) { long timestamp = genTime(); while (timestamp <= lastTimestamp) { timestamp = genTime(); } return timestamp; } private long genTime () { return System.currentTimeMillis(); } public static void main (String[] args) { Snowflake idWorker = new Snowflake(1 , 1 ); long startTime = System.nanoTime(); for (int i = 0 ; i < 10 ; i++) { long id = idWorker.nextId(); System.out.println(id); } System.out.println((System.nanoTime() - startTime) / 1000000 + "ms" ); } }
SnowFlake算法优缺点
优点
高性能高可用:生成时不依赖于数据库,完全在内存中生成。
容量大:每秒中能生成数百万的自增ID。
ID自增:存入数据库中,索引效率高。
缺点
依赖与系统时间的一致性,如果系统时间被回调,或者改变,可能会造成id冲突或者重复(可以用百度开源的分布式唯一ID生成器 UidGenerator或者美团开源的分布式ID生成系统 Leaf解决)
在分布式系统中,所有的机器可能时间不同步,会导致Id不是全局递增的(正常我们只需要趋势递增就OK)。