Twitter的雪花算法SnowFlake,使用Java語言實現。java
SnowFlake算法產生的ID是一個64位的整型,結構以下(每一部分用「-」符號分隔):算法
1位標識部分,在java中因爲long的最高位是符號位,正數是0,負數是1,通常生成的ID爲正數,因此爲0;app
41位時間戳部分,這個是毫秒級的時間,通常實現上不會存儲當前的時間戳,而是時間戳的差值(當前時間-固定的開始時間),這樣可使產生的ID從更小值開始;41位的時間戳可使用69年,(1L << 41) / (1000L * 60 * 60 * 24 * 365) = 69年;less
10位節點部分,Twitter實現中使用前5位做爲數據中心標識,後5位做爲機器標識,能夠部署1024個節點;分佈式
12位序列號部分,支持同一毫秒內同一個節點能夠生成4096個ID;ide
SnowFlake算法生成的ID大體上是按照時間遞增的,用在分佈式系統中時,須要注意數據中心標識和機器標識必須惟一,這樣就能保證每一個節點生成的ID都是惟一的。或許咱們不必定都須要像上面那樣使用5位做爲數據中心標識,5位做爲機器標識,能夠根據咱們業務的須要,靈活分配節點部分,如:若不須要數據中心,徹底可使用所有10位做爲機器標識;若數據中心很少,也能夠只使用3位做爲數據中心,7位做爲機器標識。this
snowflake生成的ID總體上按照時間自增排序,而且整個分佈式系統內不會產生ID碰撞(由datacenter和workerId做區分),而且效率較高。這個算法單機每秒內理論上最多能夠生成1000*(2^12),也就是409.6萬個ID。spa
/** * 描述: Twitter的分佈式自增ID雪花算法snowflake (Java版) * * @author jinzg * @create 2018-03-14 12:37 **/ public class IdWorker { /** * 起始的時間戳 */ private final long twepoch = Date .from(LocalDate.of(2018, 1, 1).atStartOfDay() .atZone(ZoneId.systemDefault()).toInstant()).getTime(); /** * 每一部分佔用的位數 */ private final long sequenceBits = 12L; 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 sequenceMask = -1L ^ (-1L << sequenceBits); /** * 每一部分向左的位移 */ private final long workerIdShift = sequenceBits; private final long datacenterIdShift = sequenceBits + workerIdBits; private final long timestampLeftShift = sequenceBits + workerIdBits + datacenterIdBits; private long workerId;// 數據中心 private long datacenterId;// 機器標識 private long sequence = 0L;// 序列號 private long lastTimestamp = -1L;// 上一次時間戳 /** * Init. */ @PostConstruct public void init() { try { String ip = IpUtils.getRealIp(); if (StringUtils.isEmpty(ip)) { throw new RuntimeException("IdWorker get ip is empty"); } this.workerId = this.datacenterId = Math.abs(ip.hashCode() % 31); log.info("ip:{},workerId:{},datacenterId;{}", ip, workerId, datacenterId); } catch (SocketException e) { log.error("init error,error:{}", e); throw new RuntimeException("IdWorker init error"); } } /** * Instantiates a new Id worker. */ public IdWorker() { super(); } /** * Instantiates a new Id worker. * * @param workerId the worker id * @param datacenterId the datacenter id */ public IdWorker(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; } /** * Next id long. * * @return the long */ 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 + 1) & sequenceMask; if (sequence == 0) { timestamp = tilNextMillis(lastTimestamp); } } else { sequence = 0L; } lastTimestamp = timestamp; return ((timestamp - twepoch) << timestampLeftShift) | (datacenterId << datacenterIdShift) | (workerId << workerIdShift) | sequence; } /** * Til next millis long. * * @param lastTimestamp the last timestamp * @return the long */ protected long tilNextMillis(long lastTimestamp) { long timestamp = timeGen(); while (timestamp <= lastTimestamp) { timestamp = timeGen(); } return timestamp; } /** * Time gen long. * * @return the long */ protected long timeGen() { return System.currentTimeMillis(); } /** * test */ static class IdWorkThread implements Runnable { private Set<Long> set; private IdWorker idWorker; /** * Instantiates a new Id work thread. * * @param set the set * @param idWorker the id worker */ public IdWorkThread(Set<Long> set, IdWorker idWorker) { this.set = set; this.idWorker = idWorker; } @Override public void run() { while (true) { long id = idWorker.nextId(); System.out.println(Thread.currentThread().getName() + ":" + id); if (!set.add(id)) { System.out.println("duplicate:" + id); } } } } /** * The entry point of application. * * @param args the input arguments */ public static void main(String[] args) { Set<Long> set = new HashSet<Long>(); final IdWorker idWorker1 = new IdWorker(0, 0); final IdWorker idWorker2 = new IdWorker(1, 0); Thread t1 = new Thread(new IdWorkThread(set, idWorker1)); Thread t2 = new Thread(new IdWorkThread(set, idWorker2)); t1.setDaemon(true); t2.setDaemon(true); t1.start(); t2.start(); try { Thread.sleep(10000); } catch (InterruptedException e) { e.printStackTrace(); } } }
優勢:code
缺點:orm