Snow Leaf ID Playground

互動學習 Snowflake 與 Leaf-Segment 分散式 ID 生成算法

為什麼需要分散式 ID?

在分散式系統中,傳統的自增 ID 面臨諸多挑戰

傳統自增 ID 的問題

  • 單點故障風險 - 依賴單一資料庫
  • 效能瓶頸 - 無法水平擴展
  • 分庫分表困難 - ID 可能重複
  • 安全性問題 - 易被猜測遍歷

分散式 ID 的需求

  • 全局唯一性 - 跨機器不重複
  • 趨勢遞增 - 利於 B+ Tree 索引
  • 高可用性 - 無單點故障
  • 高效能 - 支援高併發場景

主流解決方案

Snowflake - Twitter 開源的 64-bit ID 算法,基於時間戳記,無需外部依賴。

Leaf-Segment - 美團開源的號段模式,基於資料庫預分配,ID 連續遞增。

Snowflake 算法

Twitter 於 2010 年開源的 64-bit 分散式 ID 生成算法

算法原理

Snowflake 將 64-bit 整數劃分為多個區塊,透過時間戳記、機器識別碼和序列號的組合,確保每個 ID 的唯一性。每秒可生成約 409.6 萬 個 ID(單機)。

核心特點:
  • 無需中央協調器,本地生成
  • 時間戳記確保趨勢遞增
  • 機器 ID 確保分散式唯一性
  • 序列號確保同毫秒內唯一性

64-bit 位元結構

點擊各區塊查看詳細說明

互動式 ID 生成器

生成的 ID
-

ID 解析

輸入 ID 後點擊解析按鈕

批量生成

點擊批量生成按鈕

Leaf-Segment 算法

美團開源的基於號段模式的分散式 ID 解決方案

算法原理

Leaf-Segment 採用預分配號段的方式,每次從資料庫獲取一個號段(例如 1-1000),在記憶體中遞增分配。搭配雙 Buffer 機制,確保高可用性。

核心特點:
  • ID 完全連續遞增
  • 雙 Buffer 確保無阻塞
  • 支援多業務標籤隔離
  • 資料庫故障時仍可短暫服務

資料庫表結構

雙 Buffer 機制

當 Buffer 1 使用到閾值時,會自動預載入 Buffer 2

互動式模擬器

當前 ID
-

點擊初始化按鈕開始模擬

算法比較與選擇指南

根據您的場景需求選擇最適合的方案

詳細比較

比較維度 Snowflake Leaf-Segment
ID 長度 64-bit (Long) 可配置 (通常 64-bit)
外部依賴 無(純演算法) 資料庫
時鐘依賴 是(需處理回撥)
ID 遞增特性 趨勢遞增(同機器內) 完全連續遞增
可讀性 低(二進位編碼) 高(純數字)
效能 極高(本地生成) 高(有網路開銷)
運維複雜度 中(需維護 DB)
容錯能力 高(無單點) 中(依賴 DB)
每秒生成量(單機) ~409 萬 取決於號段大小

選擇決策樹

回答幾個簡單問題,找到最適合您的方案

❄️

Snowflake 適用場景

  • 高併發、低延遲要求
  • 無法依賴外部服務
  • 分散式微服務架構
  • ID 不需要連續
  • 需要從 ID 反解時間資訊
🍃

Leaf-Segment 適用場景

  • 需要連續遞增的 ID
  • 有現成的資料庫基礎設施
  • 需要高可讀性的 ID
  • 多業務需要隔離的 ID 序列
  • 對時鐘同步有顧慮

實作建議與延伸閱讀

開始在您的專案中實作分散式 ID

Snowflake 實作範例

public class SnowflakeGenerator {
    private final long epoch = 1577836800000L; // 2020-01-01
    private final long datacenterIdBits = 5L;
    private final long workerIdBits = 5L;
    private final long sequenceBits = 12L;

    private final long maxDatacenterId = ~(-1L << datacenterIdBits);
    private final long maxWorkerId = ~(-1L << workerIdBits);
    private final long sequenceMask = ~(-1L << sequenceBits);

    private final long workerIdShift = sequenceBits;
    private final long datacenterIdShift = sequenceBits + workerIdBits;
    private final long timestampShift = sequenceBits + workerIdBits + datacenterIdBits;

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

    public SnowflakeGenerator(long datacenterId, long workerId) {
        this.datacenterId = datacenterId;
        this.workerId = workerId;
    }

    public synchronized long nextId() {
        long timestamp = System.currentTimeMillis();

        if (timestamp < lastTimestamp) {
            throw new RuntimeException("Clock moved backwards");
        }

        if (timestamp == lastTimestamp) {
            sequence = (sequence + 1) & sequenceMask;
            if (sequence == 0) {
                timestamp = waitNextMillis(lastTimestamp);
            }
        } else {
            sequence = 0L;
        }

        lastTimestamp = timestamp;

        return ((timestamp - epoch) << timestampShift)
                | (datacenterId << datacenterIdShift)
                | (workerId << workerIdShift)
                | sequence;
    }

    private long waitNextMillis(long lastTimestamp) {
        long timestamp = System.currentTimeMillis();
        while (timestamp <= lastTimestamp) {
            timestamp = System.currentTimeMillis();
        }
        return timestamp;
    }
}
import time
import threading

class SnowflakeGenerator:
    def __init__(self, datacenter_id=1, worker_id=1):
        self.epoch = 1577836800000  # 2020-01-01
        self.datacenter_id = datacenter_id
        self.worker_id = worker_id
        self.sequence = 0
        self.last_timestamp = -1
        self.lock = threading.Lock()

        # Bit lengths
        self.sequence_bits = 12
        self.worker_id_bits = 5
        self.datacenter_id_bits = 5

        # Max values
        self.max_sequence = (1 << self.sequence_bits) - 1

        # Bit shifts
        self.worker_id_shift = self.sequence_bits
        self.datacenter_id_shift = self.sequence_bits + self.worker_id_bits
        self.timestamp_shift = self.sequence_bits + self.worker_id_bits + self.datacenter_id_bits

    def _current_millis(self):
        return int(time.time() * 1000)

    def next_id(self):
        with self.lock:
            timestamp = self._current_millis()

            if timestamp < self.last_timestamp:
                raise Exception("Clock moved backwards")

            if timestamp == self.last_timestamp:
                self.sequence = (self.sequence + 1) & self.max_sequence
                if self.sequence == 0:
                    while timestamp <= self.last_timestamp:
                        timestamp = self._current_millis()
            else:
                self.sequence = 0

            self.last_timestamp = timestamp

            return (
                ((timestamp - self.epoch) << self.timestamp_shift) |
                (self.datacenter_id << self.datacenter_id_shift) |
                (self.worker_id << self.worker_id_shift) |
                self.sequence
            )

# Usage
generator = SnowflakeGenerator(datacenter_id=1, worker_id=1)
new_id = generator.next_id()
print(f"Generated ID: {new_id}")
class SnowflakeGenerator {
  constructor(options = {}) {
    this.epoch = options.epoch || new Date('2020-01-01').getTime();
    this.datacenterId = BigInt(options.datacenterId || 1);
    this.workerId = BigInt(options.workerId || 1);
    this.sequence = 0n;
    this.lastTimestamp = -1n;

    // Bit lengths
    this.sequenceBits = 12n;
    this.workerIdBits = 5n;
    this.datacenterIdBits = 5n;

    // Max values
    this.maxSequence = (1n << this.sequenceBits) - 1n;

    // Bit shifts
    this.workerIdShift = this.sequenceBits;
    this.datacenterIdShift = this.sequenceBits + this.workerIdBits;
    this.timestampShift = this.sequenceBits + this.workerIdBits + this.datacenterIdBits;
  }

  generate() {
    let timestamp = BigInt(Date.now() - this.epoch);

    if (timestamp < this.lastTimestamp) {
      throw new Error('Clock moved backwards');
    }

    if (timestamp === this.lastTimestamp) {
      this.sequence = (this.sequence + 1n) & this.maxSequence;
      if (this.sequence === 0n) {
        while (timestamp <= this.lastTimestamp) {
          timestamp = BigInt(Date.now() - this.epoch);
        }
      }
    } else {
      this.sequence = 0n;
    }

    this.lastTimestamp = timestamp;

    return (
      (timestamp << this.timestampShift) |
      (this.datacenterId << this.datacenterIdShift) |
      (this.workerId << this.workerIdShift) |
      this.sequence
    );
  }
}

// Usage
const generator = new SnowflakeGenerator({ datacenterId: 1, workerId: 1 });
const newId = generator.generate();
console.log(`Generated ID: ${newId}`);

延伸閱讀