首頁
編程語言
C語言|JAVA編程
Python編程
網頁編程
ASP編程|PHP編程
JSP編程
數據庫
MYSQL數據庫|SqlServer數據庫
Oracle數據庫|DB2數據庫
您现在的位置: 程式碼之家 >> 編程語言 >  >> Java 程式設計 >> Java 相關知識

Java 生成 UUID 與基於時間的長整型 ID 實現

     編輯:Java 相關知識

本文提供 Java 生成 UUID 的兩種標準方式(隨機 UUID、基於名字的 UUID),以及一種基於時間戳的長整型 ID 生成器,適用於分佈式系統主鍵、訂單號、追蹤 ID 等場景。

UUID 生成方式對比

類型方法特點適用場景
隨機 UUID (v4)UUID.randomUUID()122 位隨機,極低衝突概率,無序通用唯一標識、分佈式主鍵
基於名字 UUID (v3/v5)UUID.nameUUIDFromBytes()確定性:同輸入必同輸出,MD5/SHA-1同名實體去重、內容寻址
時間 UUID (v1/v2)需第三方庫含時間戳+MAC,有序,洩露 MAC 風險需時間排序的場景

完整工具類實現


import java.util.Date;
import java.util.UUID;

public class IdGenerator {

    /**
     * 生成標準隨機 UUID (版本 4)
     * @return 32 位十六進制字符串(無連字符)
     */
    public static synchronized String generateUuid() {
        UUID uuid = UUID.randomUUID();
        return uuid.toString().replace("-", "");
    }

    /**
     * 基於名字生成確定性 UUID (版本 3,MD5)
     * @param name 輸入名字(如用戶 ID、URL、內容哈希)
     * @return 32 位十六進制字符串
     */
    public static synchronized String generateUuid(String name) {
        UUID uuid = UUID.nameUUIDFromBytes(name.getBytes(StandardCharsets.UTF_8));
        return uuid.toString().replace("-", "");
    }

    /**
     * 基於時間戳生成長整型 ID
     * 格式:yyyyMMddHHmmssSSS + 2位序列號 (共 17+2=19 位)
     * 同一毫秒內最多生成 99 個不重複 ID
     * @return 19 位長整型 ID
     */
    public static synchronized long generateLongId() {
        // 1. 獲取精確到毫秒的時間戳字符串
        String timestamp = new SimpleDateFormat("yyyyMMddHHmmssSSS").format(new Date());
        
        // 2. 靜態序列號(類級別,同一毫秒內遞增)
        // 注意:分佈式環境下需引入機器 ID 或使用 Snowflake 算法
        if (sequence >= 99) sequence = 0;
        sequence++;
        
        // 3. 拼接序列號(補零)
        String seqStr = (sequence < 10) ? "0" + sequence : String.valueOf(sequence);
        
        return Long.parseLong(timestamp + seqStr);
    }

    // 靜態序列號計數器
    private static int sequence = 0;

    // 測試主方法
    public static void main(String[] args) {
        System.out.println("隨機 UUID: " + generateUuid());
        System.out.println("名字 UUID: " + generateUuid("user:1001"));
        System.out.println("時間長 ID: " + generateLongId());
        System.out.println("時間長 ID: " + generateLongId());
    }
}

進階:分佈式環境下的 ID 生成策略

問題:單機序列號在集群中會衝突

上述 generateLongId() 僅適用於單機或單寫入節點。分佈式系統需引入機器標識。

方案一:Snowflake 算法 (Twitter)


/**
 * Snowflake ID 生成器 (64 位 long)
 * 1bit 符號位 | 41bit 時間戳(毫秒) | 10bit 機器ID | 12bit 序列號
 * 支援 1024 個節點,每節點每毫秒 4096 個 ID
 */
public class SnowflakeIdGenerator {

    private final long workerId;
    private final long datacenterId;
    private long sequence = 0L;
    private long lastTimestamp = -1L;

    // 時間起始點 (2020-01-01)
    private static final long EPOCH = 1577836800000L;
    private static final long WORKER_ID_BITS = 5L;
    private static final long DATACENTER_ID_BITS = 5L;
    private static final long SEQUENCE_BITS = 12L;
    private static final long MAX_WORKER_ID = ~(-1L << WORKER_ID_BITS);
    private static final long MAX_DATACENTER_ID = ~(-1L << DATACENTER_ID_BITS);
    private static final long SEQUENCE_MASK = ~(-1L << SEQUENCE_BITS);
    private static final long WORKER_ID_SHIFT = SEQUENCE_BITS;
    private static final long DATACENTER_ID_SHIFT = SEQUENCE_BITS + WORKER_ID_BITS;
    private static final long TIMESTAMP_SHIFT = SEQUENCE_BITS + WORKER_ID_BITS + DATACENTER_ID_BITS;

    public SnowflakeIdGenerator(long workerId, long datacenterId) {
        if (workerId > MAX_WORKER_ID || workerId < 0) 
            throw new IllegalArgumentException("Worker ID 超出範圍");
        if (datacenterId > MAX_DATACENTER_ID || datacenterId < 0)
            throw new IllegalArgumentException("Datacenter ID 超出範圍");
        this.workerId = workerId;
        this.datacenterId = datacenterId;
    }

    public synchronized long nextId() {
        long timestamp = timeGen();
        if (timestamp < lastTimestamp) {
            throw new RuntimeException("時鐘回撥,拒絕生成 ID");
        }
        if (timestamp == lastTimestamp) {
            sequence = (sequence + 1) & SEQUENCE_MASK;
            if (sequence == 0) {
                timestamp = tilNextMillis(lastTimestamp);
            }
        } else {
            sequence = 0L;
        }
        lastTimestamp = timestamp;
        return ((timestamp - EPOCH) << TIMESTAMP_SHIFT)
             | (datacenterId << DATACENTER_ID_SHIFT)
             | (workerId << WORKER_ID_SHIFT)
             | sequence;
    }

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

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

方案二:百度 UidGenerator / 美團 Leaf

  • UidGenerator:基於 Snowflake 改進,支援 RingBuffer 預生成、時間回撥容忍
  • Leaf:號段模式(DB 分配段)+ Snowflake 雙模式,支援動態擴容

方案三:資料庫號段模式


-- 表結構
CREATE TABLE id_segment (
    biz_tag VARCHAR(128) PRIMARY KEY,
    max_id BIGINT NOT NULL,
    step INT NOT NULL DEFAULT 1000,
    version INT NOT NULL DEFAULT 0
);

-- 獲取 ID 段 (樂觀鎖)
UPDATE id_segment 
SET max_id = max_id + step, version = version + 1
WHERE biz_tag = 'ORDER' AND version = #{version};

-- 應用端緩存當前段,用完再取下一段

選型建議

場景推薦方案理由
單機/微服務少量節點時間戳+序列號 / UUID簡單、無依賴
高併發分佈式系統Snowflake / UidGenerator高性能、有序、去中心化
嚴格遞增、可審計資料庫號段模式強一致、易監控
業務語義化 ID前綴 + 時間 + 序列 (如 ORD202401150001)可讀性強
內容去重/寻址基於名字 UUID (v3/v5)確定性

總結

  • UUID.randomUUID() 是最通用的選擇,無狀態、無協調
  • 需「有序、數值型、短」ID 時,Snowflake 為首選
  • 分佈式環境務必解決「機器 ID 分配」與「時鐘回撥」問題
  • 業務主鍵建議加上業務前綴,提升可讀性與排查效率
  1. 上一篇文章:
  2. 下一篇文章:
Copyright © 程式碼之家 All Rights Reserved