1. 程式人生 > >分散式唯一ID生成演算法-雪花演算法

分散式唯一ID生成演算法-雪花演算法

在我們的工作中,資料庫某些表的欄位會用到唯一的,趨勢遞增的訂單編號,我們將介紹兩種方法,一種是傳統的採用隨機數生成的方式,另外一種是採用當前比較流行的“分散式唯一ID生成演算法-雪花演算法”來實現。

 

一、時間戳隨機數生成唯一ID

我們寫一個for迴圈,用RandomUtil.generateOrderCode()生成1000個唯一ID,執行結果我們會發現出現重複的ID。

/**
 * 隨機數生成util
 **/
public class RandomUtil {
    private static final SimpleDateFormat dateFormatOne=new SimpleDateFormat("yyyyMMddHHmmssSS");
 
    private static final ThreadLocalRandom random=ThreadLocalRandom.current();
    //生成訂單編號-方式一
    public static String generateOrderCode(){
        //TODO:時間戳+N為隨機數流水號
        return dateFormatOne.format(DateTime.now().toDate()) + generateNumber(4);
    }
 
    //N為隨機數流水號
    public static String generateNumber(final int num){
        StringBuffer sb=new StringBuffer();
        for (int i=1;i<=num;i++){
            sb.append(random.nextInt(9));
        }
        return sb.toString();
    }
}

鑑於此種“基於隨機數生成”的方式在高併發的場景下並不符合我們的要求,接下來,我們將介紹另外一種比較流行的、典型的方式,即“分散式唯一ID生成演算法-雪花演算法”來實現。

對於“雪花演算法”的介紹,各位小夥伴可以參考Github上的這一連結,我覺得講得還是挺清晰的:https://github.com/souyunku/SnowFlake,詳細的Debug在這裡就不贅述了,下面截取了部分概述:

 

二、分散式唯一ID生成演算法-雪花演算法

我們寫一個for迴圈,用SNOW_FLAKE.nextId() 生成1000個唯一ID,發現不會出現重複的。

 /** * 雪花演算法
*/ public class SnowFlake { //起始的時間戳 private final static long START_STAMP = 1480166465631L; //每一部分佔用的位數 private final static long SEQUENCE_BIT = 12; //序列號佔用的位數 private final static long MACHINE_BIT = 5; //機器標識佔用的位數 private final static long DATA_CENTER_BIT = 5;//資料中心佔用的位數 //每一部分的最大值 private final static long MAX_DATA_CENTER_NUM = -1L ^ (-1L << DATA_CENTER_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 DATA_CENTER_LEFT = SEQUENCE_BIT + MACHINE_BIT; private final static long TIMESTAMP_LEFT = DATA_CENTER_LEFT + DATA_CENTER_BIT; private long dataCenterId; //資料中心 private long machineId; //機器標識 private long sequence = 0L; //序列號 private long lastStamp = -1L;//上一次時間戳 public SnowFlake(long dataCenterId, long machineId) { if (dataCenterId > MAX_DATA_CENTER_NUM || dataCenterId < 0) { throw new IllegalArgumentException("dataCenterId can't be greater than MAX_DATA_CENTER_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 public synchronized long nextId() { long currStamp = getNewStamp(); if (currStamp < lastStamp) { throw new RuntimeException("Clock moved backwards. Refusing to generate id"); } if (currStamp == lastStamp) { //相同毫秒內,序列號自增 sequence = (sequence + 1) & MAX_SEQUENCE; //同一毫秒的序列數已經達到最大 if (sequence == 0L) { currStamp = getNextMill(); } } else { //不同毫秒內,序列號置為0 sequence = 0L; } lastStamp = currStamp; return (currStamp - START_STAMP) << TIMESTAMP_LEFT //時間戳部分 | dataCenterId << DATA_CENTER_LEFT //資料中心部分 | machineId << MACHINE_LEFT //機器標識部分 | sequence; //序列號部分 } private long getNextMill() { long mill = getNewStamp(); while (mill <= lastStamp) { mill = getNewStamp(); } return mill; } private long getNewStamp() { return System.currentTimeMillis(); } }

綜上,我們在高併發大量生成唯一ID時,避免生成重複ID,需要用第二種雪花演算法生成。

&n