在分散式系統中,傳統的自增 ID 面臨諸多挑戰
Snowflake - Twitter 開源的 64-bit ID 算法,基於時間戳記,無需外部依賴。
Leaf-Segment - 美團開源的號段模式,基於資料庫預分配,ID 連續遞增。
Twitter 於 2010 年開源的 64-bit 分散式 ID 生成算法
Snowflake 將 64-bit 整數劃分為多個區塊,透過時間戳記、機器識別碼和序列號的組合,確保每個 ID 的唯一性。每秒可生成約 409.6 萬 個 ID(單機)。
點擊各區塊查看詳細說明
輸入 ID 後點擊解析按鈕
點擊批量生成按鈕
美團開源的基於號段模式的分散式 ID 解決方案
Leaf-Segment 採用預分配號段的方式,每次從資料庫獲取一個號段(例如 1-1000),在記憶體中遞增分配。搭配雙 Buffer 機制,確保高可用性。
當 Buffer 1 使用到閾值時,會自動預載入 Buffer 2
點擊初始化按鈕開始模擬
根據您的場景需求選擇最適合的方案
| 比較維度 | Snowflake | Leaf-Segment |
|---|---|---|
| ID 長度 | 64-bit (Long) | 可配置 (通常 64-bit) |
| 外部依賴 | 無(純演算法) | 資料庫 |
| 時鐘依賴 | 是(需處理回撥) | 否 |
| ID 遞增特性 | 趨勢遞增(同機器內) | 完全連續遞增 |
| 可讀性 | 低(二進位編碼) | 高(純數字) |
| 效能 | 極高(本地生成) | 高(有網路開銷) |
| 運維複雜度 | 低 | 中(需維護 DB) |
| 容錯能力 | 高(無單點) | 中(依賴 DB) |
| 每秒生成量(單機) | ~409 萬 | 取決於號段大小 |
回答幾個簡單問題,找到最適合您的方案
開始在您的專案中實作分散式 ID
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}`);