????????雪花算法(Snowflake)作為一種分布式 ID 生成方案,在分布式系統中具有顯著優勢,能夠解決多個關鍵問題。以下是它的核心好處及主要應用場景:
雪花算法的核心好處
- 全局唯一性:通過時間戳、機器 ID、數據中心 ID 和序列號的組合,確保在分布式環境下生成的 ID 絕對唯一
- 時間有序性:ID 包含時間戳信息,整體按時間趨勢遞增,對數據庫索引友好
- 高性能:本地計算生成,無需網絡請求,單機每秒可生成百萬級 ID
- 無依賴:不依賴第三方中間件,節點可獨立工作,降低系統復雜度
- 可擴展:支持最多 1024 個節點部署,可通過調整位數分配適配不同規模集群
- 節省空間:64 位長整數存儲,比 UUID 更節省空間,計算和傳輸效率更高
雪花算法的主要應用場景
- 分布式數據庫的全局唯一主鍵
- 訂單系統的訂單號生成
- 日志系統的追蹤 ID
- 高并發場景下的 ID 生成(如秒殺、搶購)
- 分布式任務調度的任務 ID
- 消息隊列的消息 ID
完整 Java 實現代碼
public class SnowflakeIdGenerator {// 起始時間戳 (2020-01-01 00:00:00)private final long START_TIMESTAMP = 1577808000000L;// 機器ID所占的位數private final long WORKER_ID_BITS = 5L;// 數據中心ID所占的位數private final long DATA_CENTER_ID_BITS = 5L;// 支持的最大機器IDprivate final long MAX_WORKER_ID = ~(-1L << WORKER_ID_BITS);// 支持的最大數據中心IDprivate final long MAX_DATA_CENTER_ID = ~(-1L << DATA_CENTER_ID_BITS);// 序列在ID中占的位數private final long SEQUENCE_BITS = 12L;// 機器ID向左移12位private final long WORKER_ID_SHIFT = SEQUENCE_BITS;// 數據中心ID向左移17位(12+5)private final long DATA_CENTER_ID_SHIFT = SEQUENCE_BITS + WORKER_ID_BITS;// 時間戳向左移22位(5+5+12)private final long TIMESTAMP_LEFT_SHIFT = SEQUENCE_BITS + WORKER_ID_BITS + DATA_CENTER_ID_BITS;// 生成序列的掩碼,這里為4095private final long SEQUENCE_MASK = ~(-1L << SEQUENCE_BITS);private long workerId; // 機器IDprivate long dataCenterId; // 數據中心IDprivate long sequence = 0L; // 毫秒內序列private long lastTimestamp = -1L; // 上次生成ID的時間戳/*** 構造函數* @param workerId 機器ID (0~31)* @param dataCenterId 數據中心ID (0~31)*/public SnowflakeIdGenerator(long workerId, long dataCenterId) {if (workerId > MAX_WORKER_ID || workerId < 0) {throw new IllegalArgumentException(String.format("workerId can't be greater than %d or less than 0", MAX_WORKER_ID));}if (dataCenterId > MAX_DATA_CENTER_ID || dataCenterId < 0) {throw new IllegalArgumentException(String.format("dataCenterId can't be greater than %d or less than 0", MAX_DATA_CENTER_ID));}this.workerId = workerId;this.dataCenterId = dataCenterId;}/*** 生成下一個ID* @return 雪花算法生成的唯一ID*/public synchronized long nextId() {long timestamp = System.currentTimeMillis();// 處理系統時鐘回退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) & SEQUENCE_MASK;// 序列號溢出,等待下一毫秒if (sequence == 0) {timestamp = tilNextMillis(lastTimestamp);}} else {// 不同時間戳,序列號重置sequence = 0L;}// 更新上次生成ID的時間戳lastTimestamp = timestamp;// 組合生成IDreturn ((timestamp - START_TIMESTAMP) << TIMESTAMP_LEFT_SHIFT)| (dataCenterId << DATA_CENTER_ID_SHIFT)| (workerId << WORKER_ID_SHIFT)| sequence;}/*** 等待到下一毫秒*/private long tilNextMillis(long lastTimestamp) {long timestamp = System.currentTimeMillis();while (timestamp <= lastTimestamp) {timestamp = System.currentTimeMillis();}return timestamp;}// 測試public static void main(String[] args) {SnowflakeIdGenerator idGenerator = new SnowflakeIdGenerator(1, 1);// 生成10個IDfor (int i = 0; i < 10; i++) {long id = idGenerator.nextId();System.out.println("生成的ID: " + id);}}
}
????????使用時,只需要為每個節點分配唯一的 workerId 和 dataCenterId 組合,然后調用 nextId () 方法即可生成唯一 ID。在分布式系統中,通常可以通過配置中心或啟動參數來為每個節點分配這些 ID,確保不會重復。