實現高效加密!打造全能雪花演算法工具類

一、雪花演算法簡介

雪花演算法是一種高效、分散式全局唯一 ID 生成器,能夠生成的 ID 具有時間有序、可排序、趨勢遞增等特點,實現簡單,生成的 ID 唯一性和穩定性得到保障,廣泛應用於分散式系統中。 在對雪花演算法深入了解之前,我們先來看一下一個非常基礎的 ID 生成器。

public class IdGenerator {
    private static Long id = 0L;

    public static synchronized Long nextId() {
        return ++id;
    }
}

這個 IdGenerator 是簡單的單機 ID 生成器,通過同步保證了 ID 的唯一性。但是,這種做法有兩個問題。首先,由於 synchronized 關鍵字的使用,多線程場景下,性能會大打折扣;其次,由於是單機,可能出現重複 ID,當然這樣的概率相對較低,但是在大量生成 ID 的場景中,也無法避免。因此,需要一種分散式的、簡單的,高效的可以提升整體系統的性能和穩定性的 ID 生成器。

二、雪花演算法詳解

雪花演算法的核心是一個 64 位的 long 型數字,其中,第 1 個 bit 為不用,接下來的 41 個 bit 用於表示時間戳,最高位為符號位不用,其中高 5 個 bit 是數據中心標識,5 個 bit 是機器標識,低 12 個 bit 是序號。

public class SnowflakeIdGenerator {
    // 數據中心ID
    private long dataCenterId;
    // 機器ID
    private long machineId;
    // 序列號
    private long sequence = 0L;
    // 上一次時間戳
    private long lastTimestamp = -1L;

    public SnowflakeIdGenerator(long dataCenterId, long machineId) {
        if (dataCenterId > MAX_DATACENTER_ID || dataCenterId  MAX_MACHINE_ID || machineId < 0) {
            throw new IllegalArgumentException("MachineId can't be greater than MAX_MACHINE_ID or less than 0");
        }
        this.dataCenterId = dataCenterId;
        this.machineId = machineId;
    }

    public synchronized long nextId() {
        long currentTimestamp = timeGen();
        if (currentTimestamp < lastTimestamp) {
            throw new RuntimeException("Clock moved backwards. Refusing to generate id");
        }
        if (currentTimestamp == lastTimestamp) {
            sequence = (sequence + 1) & SEQUENCE_MASK;
            if (sequence == 0) {
                currentTimestamp = tilNextMillis(lastTimestamp);
            }
        } else {
            sequence = 0L;
        }
        lastTimestamp = currentTimestamp;
        return ((currentTimestamp - TWEPOCH) << TIMESTAMP_LEFT_SHIFT)
                | (dataCenterId << DATACENTER_ID_LEFT_SHIFT)
                | (machineId << MACHINE_ID_LEFT_SHIFT)
                | sequence;
    }

    private long tilNextMillis(long lastTimestamp) {
        long timestamp = timeGen();
        while (timestamp <= lastTimestamp) {
            timestamp = timeGen();
        }
        return timestamp;
    }

    private long timeGen() {
        return System.currentTimeMillis();
    }

    public static void main(String[] args) {
        SnowflakeIdGenerator idWorker = new SnowflakeIdGenerator(1, 1);
        for (int i = 0; i < 1000; i++) {
            long id = idWorker.nextId();
            System.out.println(Long.toBinaryString(id));
            System.out.println(id);
        }
    }
}

這個 SnowflakeIdGenerator 是一個典型的 Java 實現,它具有選取數據中心 ID 和機器 ID 的功能。這個演算法的核心是 nextId() 方法,是一個同步方法,主要包括四個部分。

第一部分獲取當前時間戳,並檢查時間戳是否被改過。

第二部分是與上一個時間戳比較,保證沒有時間上的回撥,並計算序列號。

第三部分則是更新時間戳、序號。

第四部分則是拼裝成 64 位 long 型 ID。

三、雪花演算法優化

在實際應用中,我們可能需要對雪花演算法進行迭代和優化,出於不同的業務場景,也許會有不同的優化方式。這裡我們提出一些常見的優化方式。

1. 高效的防止時鐘回撥策略

有時,時鐘回撥是不可避免的。為了消除回撥帶來的影響,我們可以採用 NTP 同步系統時鐘的方式。但是在極端情況下,毫秒級別的時鐘回撥的確會發生。比如說,在雲伺服器上,有時由於機器遷移或者其他原因時鐘可能會回撥,而這種回撥比較微小和短暫,但對於雪花演算法而言影響卻比較大。因此,為了解決這個問題,我們需要對時鐘回撥進行優化。

public synchronized long nextId() {
    long currentTimestamp = timeGen();
    if (currentTimestamp < lastTimestamp) {
        long offset = lastTimestamp - currentTimestamp;
        if (offset < MAX_BACKWARD_MS) {
            try {
                wait(offset << 1);
                currentTimestamp = timeGen();
                if (currentTimestamp < lastTimestamp) {
                    throw new RuntimeException("Clock moved backwards. Refusing to generate id");
                }
            } catch (Exception e) {
                throw new RuntimeException(e);
            }
        } else {
            throw new RuntimeException("Clock moved backwards. Refusing to generate id");
        }
    }
    if (currentTimestamp == lastTimestamp) {
        sequence = (sequence + 1) & SEQUENCE_MASK;
        if (sequence == 0) {
            currentTimestamp = tilNextMillis(lastTimestamp);
        }
    } else {
        sequence = 0L;
    }
    lastTimestamp = currentTimestamp;
    return ((currentTimestamp - TWEPOCH) << TIMESTAMP_LEFT_SHIFT)
            | (dataCenterId << DATACENTER_ID_LEFT_SHIFT)
            | (machineId << MACHINE_ID_LEFT_SHIFT)
            | sequence;
}

可以看到,在 nextId() 方法中增加了 wait() 方法,同步等待一段時間,再次獲取當前時間戳,如果時間戳依然小於上次時間戳,則拋出異常。這種方式雖然可以滿足單數時鐘回撥帶來的影響,但是多次時鐘回撥,會導致異常被拋出。因此,我們基於這個思路,可以採取更加高效的方式求解。

public synchronized long nextId() {
    long currentTimestamp = timeGen();
    if (currentTimestamp < lastTimestamp) {
        long offset = lastTimestamp - currentTimestamp;
        if (offset < MAX_BACKWARD_MS) {
            currentTimestamp = lastTimestamp;
        } else {
            throw new RuntimeException("Clock moved backwards. Refusing to generate id for " + offset + " milliseconds");
        }
    }
    if (currentTimestamp == lastTimestamp) {
        sequence = (sequence + 1) & SEQUENCE_MASK;
        if (sequence == 0) {
            currentTimestamp = tilNextMillis(lastTimestamp);
        }
    } else {
        sequence = 0L;
    }
    lastTimestamp = currentTimestamp;
    return ((currentTimestamp - TWEPOCH) << TIMESTAMP_LEFT_SHIFT)
            | (dataCenterId << DATACENTER_ID_LEFT_SHIFT)
            | (machineId << MACHINE_ID_LEFT_SHIFT)
            | sequence;
}

可以看到,只需要將等待的時間減少一半,直接使用 lastTimestamp 替代 currentTimestamp 即可,這樣的做法顯著提升了程序的性能和安全性。

2. 更為嚴格的數據安全性

在一些情況下,為了嚴格保障數據生成的唯一性,我們可能需要將 SnowflakeIdGenerator 的介面單例化,也就是說,在一個 JVM 實例上,每一個工作對象都是唯一的。

public class SnowflakeIdGenerator {
    private static volatile SnowflakeIdGenerator instance;

    public static SnowflakeIdGenerator getInstance(int dataCenterId, int machineId) {
        if (instance == null) {
            synchronized (SnowflakeIdGenerator.class) {
                if (instance == null) {
                    instance = new SnowflakeIdGenerator(dataCenterId, machineId);
                }
            }
        }
        return instance;
    }
}

這樣,一旦有重複的機器標識或者數據中心標識,就會拋出運行時異常。

四、小結

雪花演算法是一種高效、分散式全局唯一 ID 生成器,能夠生成的 ID 具有時間有序、可排序、趨勢遞增等特點,實現簡單,生成的 ID 唯一性和穩定性得到保障,廣泛應用於分散式系統中。而在雪花演算法的實現過程中,我們也可以根據不同的具體業務場景,進行一些優化和改進。

原創文章,作者:小藍,如若轉載,請註明出處:https://www.506064.com/zh-tw/n/195919.html

(0)
打賞 微信掃一掃 微信掃一掃 支付寶掃一掃 支付寶掃一掃
小藍的頭像小藍
上一篇 2024-12-02 20:37
下一篇 2024-12-02 20:37

相關推薦

  • 蝴蝶優化演算法Python版

    蝴蝶優化演算法是一種基於仿生學的優化演算法,模仿自然界中的蝴蝶進行搜索。它可以應用於多個領域的優化問題,包括數學優化、工程問題、機器學習等。本文將從多個方面對蝴蝶優化演算法Python版…

    編程 2025-04-29
  • Python字典去重複工具

    使用Python語言編寫字典去重複工具,可幫助用戶快速去重複。 一、字典去重複工具的需求 在使用Python編寫程序時,我們經常需要處理數據文件,其中包含了大量的重複數據。為了方便…

    編程 2025-04-29
  • Python實現爬樓梯演算法

    本文介紹使用Python實現爬樓梯演算法,該演算法用於計算一個人爬n級樓梯有多少種不同的方法。 有一樓梯,小明可以一次走一步、兩步或三步。請問小明爬上第 n 級樓梯有多少種不同的爬樓梯…

    編程 2025-04-29
  • AES加密解密演算法的C語言實現

    AES(Advanced Encryption Standard)是一種對稱加密演算法,可用於對數據進行加密和解密。在本篇文章中,我們將介紹C語言中如何實現AES演算法,並對實現過程進…

    編程 2025-04-29
  • Harris角點檢測演算法原理與實現

    本文將從多個方面對Harris角點檢測演算法進行詳細的闡述,包括演算法原理、實現步驟、代碼實現等。 一、Harris角點檢測演算法原理 Harris角點檢測演算法是一種經典的計算機視覺演算法…

    編程 2025-04-29
  • 數據結構與演算法基礎青島大學PPT解析

    本文將從多個方面對數據結構與演算法基礎青島大學PPT進行詳細的闡述,包括數據類型、集合類型、排序演算法、字元串匹配和動態規劃等內容。通過對這些內容的解析,讀者可以更好地了解數據結構與算…

    編程 2025-04-29
  • 瘦臉演算法 Python 原理與實現

    本文將從多個方面詳細闡述瘦臉演算法 Python 實現的原理和方法,包括該演算法的意義、流程、代碼實現、優化等內容。 一、演算法意義 隨著科技的發展,瘦臉演算法已經成為了人們修圖中不可缺少…

    編程 2025-04-29
  • 如何通過jstack工具列出假死的java進程

    假死的java進程是指在運行過程中出現了某些問題導致進程停止響應,此時無法通過正常的方式關閉或者重啟該進程。在這種情況下,我們可以藉助jstack工具來獲取該進程的進程號和線程號,…

    編程 2025-04-29
  • 註冊表取證工具有哪些

    註冊表取證是數字取證的重要分支,主要是獲取計算機系統中的註冊表信息,進而分析痕迹,獲取重要證據。本文將以註冊表取證工具為中心,從多個方面進行詳細闡述。 一、註冊表取證工具概述 註冊…

    編程 2025-04-29
  • 神經網路BP演算法原理

    本文將從多個方面對神經網路BP演算法原理進行詳細闡述,並給出完整的代碼示例。 一、BP演算法簡介 BP演算法是一種常用的神經網路訓練演算法,其全稱為反向傳播演算法。BP演算法的基本思想是通過正…

    編程 2025-04-29

發表回復

登錄後才能評論