分佈式的惟一ID是一個系統設計過程當中常常遇到的問題。生成分佈式ID的策略有多種,應用場景也不盡相同。下面簡單介紹一下一些經常使用的分佈式ID生成策略:java
經過定義數據庫的自增ID進行實現。git
優勢:github
缺點:redis
優化:算法
經過本地代碼或者數據庫生成。數據庫
優勢:架構
缺點:less
優化:dom
private static BigInteger getBigIntegerFromUuid() { UUID id = UUID.randomUUID(); ByteBuffer bb = ByteBuffer.wrap(new byte[16]); bb.putLong(id.getMostSignificantBits()); bb.putLong(id.getLeastSignificantBits()); return new BigInteger(1, bb.array()); }
經過redis的incrby或者lua方式進行生成ID。分佈式
Snowflake算法源自Twitter,主要應用於解決分佈式的場景下的惟一ID。
優勢:
缺點:
改進:
下面簡單介紹一下Snowflake的基本原理。
snowflake使用64bit的存儲空間,正好是一個長整數的存儲空間。
時間前綴(42) + 機房標識(5) + 主機標識(5) + 順序號(12)
時間前綴:
使用秒數記錄。須要設置一個起始時間點(如:2016-01-01 00:00:00)。那麼生成ID時,將當前的秒數-起始時間的秒數。年T = (1L << 42) / (1000L * 60 * 60 * 24 * 365) = 139.46,表示這個算法能夠支持使用139.46年。
機房標識:
能夠用於表示機房編號之類。按(1L << 5) = 32計算,能夠支持32個機房。
主機標識:
能夠用於表示主機編號之類。按(1L << 5) = 32計算,能夠支持32個主機。綜合機房標識和主機標識,一共能夠支持使用1024臺主機。
順序號:
按(1L << 12) = 4096計算,每秒能夠生成4096個ID。再綜合機房、主機、順序號,1024*4096約400萬,即每秒能夠生成400萬個不重複的ID。
參考:https://github.com/twitter/snowflake
注意:能夠根據實際狀況對snowflake的不一樣組成位數進行調整。
參考中的代碼使用scala編寫,下面是java版本的翻譯:
package com.zheng.coderepo.snowflake; /** * Created by zhangchaozheng on 17-2-24. */ public class IdGen { private final long twepoch = 1288834974657L; 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 IdGen(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 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; } protected long tilNextMillis(long lastTimestamp) { long timestamp = timeGen(); while (timestamp <= lastTimestamp) { timestamp = timeGen(); } return timestamp; } protected long timeGen() { return System.currentTimeMillis(); } }