(飛歌工作筆記)分散式生成id--雪花演算法(snow)
阿新 • • 發佈:2019-01-04
雪花演算法簡單描述:
+ 最高位是符號位,始終為0,不可用。
+ 41位的時間序列,精確到毫秒級,41位的長度可以使用69年。時間位還有一個很重要的作用是可以根據時間進行排序。
+ 10位的機器標識,10位的長度最多支援部署1024個節點。
+ 12位的計數序列號,序列號即一系列的自增id,可以支援同一節點同一毫秒生成多個ID序號,12位的計數序列號支援每個節點每毫秒產生4096個ID序號。
看的出來,這個演算法很簡潔也很簡單,但依舊是一個很好的ID生成策略。其中,10位器識別符號一般是5位IDC+5位machine編號,唯一確定一臺機器。
我不知道Twitter是如何確定10位機器標識和12位序列號的,不過,很明顯,這是可以改變的。
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 = 1508143349995L; /** * 可分配的位數 */ private final static int REMAIN_BIT_NUM = 22; /** * 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; } }