分布式ID生成 - 雪花算法

本文章僅供小編學(xué)習(xí)使用,如有侵犯他人版權(quán),請(qǐng)聯(lián)系小編撤回或刪除

項(xiàng)目中主鍵ID生成方式比較多,但是哪種方式更能提高的我們的工作效率、項(xiàng)目質(zhì)量、代碼實(shí)用性以及健壯性呢,下面作了一下比較,目前雪花算法的優(yōu)點(diǎn)還是很明顯的。

優(yōu)缺點(diǎn)比較

  • UUID(缺點(diǎn):太長(zhǎng)、沒法排序、使數(shù)據(jù)庫(kù)性能降低)
  • Redis(缺點(diǎn):必須依賴Redis)
  • Oracle序列號(hào)(缺點(diǎn):用Oracle才能使用)
  • Snowflake雪花算法,優(yōu)點(diǎn):生成有順序的id,提高數(shù)據(jù)庫(kù)的性能

Snowflake雪花算法解析

18501915-4a8c76297cc4c55a.png

雪花算法解析 結(jié)構(gòu) snowflake的結(jié)構(gòu)如下(每部分用-分開):
0 - 0000000000 0000000000 0000000000 0000000000 0 - 00000 - 00000 - 000000000000
第一位為未使用,接下來(lái)的41位為毫秒級(jí)時(shí)間(41位的長(zhǎng)度可以使用69年),

然后是5位datacenterId和5位workerId(10位的長(zhǎng)度最多支持部署1024個(gè)節(jié)點(diǎn)) ,

最后12位是毫秒內(nèi)的計(jì)數(shù)(12位的計(jì)數(shù)順序號(hào)支持每個(gè)節(jié)點(diǎn)每毫秒產(chǎn)生4096個(gè)ID序號(hào))

一共加起來(lái)剛好64位,為一個(gè)Long型。(轉(zhuǎn)換成字符串長(zhǎng)度為18)。

Snowflake算法核心把時(shí)間戳,工作機(jī)器id,序列號(hào)組合在一起。

整體上按照時(shí)間自增排序,并且整個(gè)分布式系統(tǒng)內(nèi)不會(huì)產(chǎn)生ID碰撞(由datacenter和機(jī)器ID作區(qū)分),

并且效率較高,經(jīng)測(cè)試,snowflake每秒能夠產(chǎn)生26萬(wàn)ID左右,完全滿足需要。

分布式Snowflake雪花算法代碼

package com.mishi.springcloudconfigserver.utils;

import org.apache.commons.lang3.RandomUtils;
import org.apache.commons.lang3.StringUtils;

import java.net.Inet4Address;
import java.net.UnknownHostException;

/**
 * @Author liuzongqiang
 * SnowFlake的結(jié)構(gòu)如下(每部分用-分開):<br>
 * 0 - 0000000000 0000000000 0000000000 0000000000 0 - 00000 - 00000 - 000000000000 <br>
 * 1位標(biāo)識(shí),由于long基本類型在Java中是帶符號(hào)的,最高位是符號(hào)位,正數(shù)是0,負(fù)數(shù)是1,所以id一般是正數(shù),最高位是0<br>
 * 41位時(shí)間截(毫秒級(jí)),注意,41位時(shí)間截不是存儲(chǔ)當(dāng)前時(shí)間的時(shí)間截,而是存儲(chǔ)時(shí)間截的差值(當(dāng)前時(shí)間截 - 開始時(shí)間截)
 * 得到的值),這里的的開始時(shí)間截,一般是我們的id生成器開始使用的時(shí)間,由我們程序來(lái)指定的(如下下面程序IdWorker類的startTime屬性)。41位的時(shí)間截,可以使用69年,年T = (1L << 41) / (1000L * 60 * 60 * 24 * 365) = 69<br>
 * 10位的數(shù)據(jù)機(jī)器位,可以部署在1024個(gè)節(jié)點(diǎn),包括5位datacenterId和5位workerId<br>
 * 12位序列,毫秒內(nèi)的計(jì)數(shù),12位的計(jì)數(shù)順序號(hào)支持每個(gè)節(jié)點(diǎn)每毫秒(同一機(jī)器,同一時(shí)間截)產(chǎn)生4096個(gè)ID序號(hào)<br>
 * 加起來(lái)剛好64位,為一個(gè)Long型。<br>
 * SnowFlake的優(yōu)點(diǎn)是,整體上按照時(shí)間自增排序,并且整個(gè)分布式系統(tǒng)內(nèi)不會(huì)產(chǎn)生ID碰撞(由數(shù)據(jù)中心ID和機(jī)器ID作區(qū)分),并且效率較高,經(jīng)測(cè)試,SnowFlake每秒能夠產(chǎn)生26萬(wàn)ID左右。 
 * @Date 2019/7/20 0020 18:13
 * @Version 1.0
 **/
public class SnowFlakeGenerator {
// ==============================Fields===========================================
    /**
     * 開始時(shí)間截 (2015-01-01)
     */
    private final long twepoch = 1489111610226L;

    /**
     * 機(jī)器id所占的位數(shù)
     */
    private final long workerIdBits = 5L;

    /**
     * 數(shù)據(jù)標(biāo)識(shí)id所占的位數(shù)
     */
    private final long dataCenterIdBits = 5L;

    /**
     * 支持的最大機(jī)器id,結(jié)果是31 (這個(gè)移位算法可以很快的計(jì)算出幾位二進(jìn)制數(shù)所能表示的最大十進(jìn)制數(shù))
     */
    private final long maxWorkerId = -1L ^ (-1L << workerIdBits);

    /**
     * 支持的最大數(shù)據(jù)標(biāo)識(shí)id,結(jié)果是31
     */
    private final long maxDataCenterId = -1L ^ (-1L << dataCenterIdBits);

    /**
     * 序列在id中占的位數(shù)
     */
    private final long sequenceBits = 12L;

    /**
     * 機(jī)器ID向左移12位
     */
    private final long workerIdShift = sequenceBits;

    /**
     * 數(shù)據(jù)標(biāo)識(shí)id向左移17位(12+5)
     */
    private final long dataCenterIdShift = sequenceBits + workerIdBits;

    /**
     * 時(shí)間截向左移22位(5+5+12)
     */
    private final long timestampLeftShift = sequenceBits + workerIdBits + dataCenterIdBits;

    /**
     * 生成序列的掩碼,這里為4095 (0b111111111111=0xfff=4095)
     */
    private final long sequenceMask = -1L ^ (-1L << sequenceBits);

    /**
     * 工作機(jī)器ID(0~31)
     */
    private long workerId;

    /**
     * 數(shù)據(jù)中心ID(0~31)
     */
    private long dataCenterId;

    /**
     * 毫秒內(nèi)序列(0~4095)
     */
    private long sequence = 0L;

    /**
     * 上次生成ID的時(shí)間截
     */
    private long lastTimestamp = -1L;

    private static SnowFlakeGenerator idWorker;

    static {
        idWorker = new SnowFlakeGenerator(getWorkId(), getDataCenterId());
    }

    //==============================Constructors=====================================

    /**
     * 構(gòu)造函數(shù)
     *
     * @param workerId     工作ID (0~31)
     * @param dataCenterId 數(shù)據(jù)中心ID (0~31)
     */
    public SnowFlakeGenerator(long workerId, long dataCenterId) {
        if (workerId > maxWorkerId || workerId < 0) {
            throw new IllegalArgumentException(String.format("workerId can't be greater than %d or less than 0", maxWorkerId));
        }
        if (dataCenterId > maxDataCenterId || dataCenterId < 0) {
            throw new IllegalArgumentException(String.format("dataCenterId can't be greater than %d or less than 0", maxDataCenterId));
        }
        this.workerId = workerId;
        this.dataCenterId = dataCenterId;
    }

    // ==============================Methods==========================================

    /**
     * 獲得下一個(gè)ID (該方法是線程安全的)
     *
     * @return SnowflakeId
     */
    public synchronized long nextId() {
        long timestamp = timeGen();

        //如果當(dāng)前時(shí)間小于上一次ID生成的時(shí)間戳,說(shuō)明系統(tǒng)時(shí)鐘回退過(guò)這個(gè)時(shí)候應(yīng)當(dāng)拋出異常
        if (timestamp < lastTimestamp) {
            throw new RuntimeException(
                    String.format("Clock moved backwards.  Refusing to generate id for %d milliseconds", lastTimestamp - timestamp));
        }

        //如果是同一時(shí)間生成的,則進(jìn)行毫秒內(nèi)序列
        if (lastTimestamp == timestamp) {
            sequence = (sequence + 1) & sequenceMask;
            //毫秒內(nèi)序列溢出
            if (sequence == 0) {
                //阻塞到下一個(gè)毫秒,獲得新的時(shí)間戳
                timestamp = tilNextMillis(lastTimestamp);
            }
        }
        //時(shí)間戳改變,毫秒內(nèi)序列重置
        else {
            sequence = 0L;
        }

        //上次生成ID的時(shí)間截
        lastTimestamp = timestamp;

        //移位并通過(guò)或運(yùn)算拼到一起組成64位的ID
        return ((timestamp - twepoch) << timestampLeftShift)
                | (dataCenterId << dataCenterIdShift)
                | (workerId << workerIdShift)
                | sequence;
    }

    /**
     * 阻塞到下一個(gè)毫秒,直到獲得新的時(shí)間戳
     *
     * @param lastTimestamp 上次生成ID的時(shí)間截
     * @return 當(dāng)前時(shí)間戳
     */
    protected long tilNextMillis(long lastTimestamp) {
        long timestamp = timeGen();
        while (timestamp <= lastTimestamp) {
            timestamp = timeGen();
        }
        return timestamp;
    }

    /**
     * 返回以毫秒為單位的當(dāng)前時(shí)間
     *
     * @return 當(dāng)前時(shí)間(毫秒)
     */
    protected long timeGen() {
        return System.currentTimeMillis();
    }

    private static Long getWorkId() {
        try {
            String hostAddress = Inet4Address.getLocalHost().getHostAddress();
            int[] ints = StringUtils.toCodePoints(hostAddress);
            int sums = 0;
            for (int b : ints) {
                sums += b;
            }
            return (long) (sums % 32);
        } catch (UnknownHostException e) {
            // 如果獲取失敗,則使用隨機(jī)數(shù)備用
            return RandomUtils.nextLong(0, 31);
        }
    }

    private static Long getDataCenterId() {
        int[] ints = StringUtils.toCodePoints("as-comment");
        int sums = 0;
        for (int i : ints) {
            sums += i;
        }
        return (long) (sums % 32);
    }


    /**
     * 靜態(tài)工具類
     *
     * @return
     */
    public static Long generateId() {
        long id = idWorker.nextId();
        return id;
    }

}

小栗子代碼:

    /**
     * 獲取隨機(jī)id
     */
    @Test
    public void snowflakeTest(){
        String id= SnowFlakeGenerator.generateId().toString();
        System.out.println(id);

    }

結(jié)果:312503943322759168

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時(shí)請(qǐng)結(jié)合常識(shí)與多方信息審慎甄別。
平臺(tái)聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點(diǎn),簡(jiǎn)書系信息發(fā)布平臺(tái),僅提供信息存儲(chǔ)服務(wù)。

友情鏈接更多精彩內(nèi)容