snowflake(雪花算法)是一個開源的分布式 ID 生成算法,結果是一個 long 型的 ID。snowflake 算法將 64bit 劃分為多段,分開來標識機器、時間等信息,具體組成結構如下圖所示:
snowflake 算法的核心思想是使用 41bit 作為毫秒數,10bit 作為機器的 ID(比如其中 5 個 bit 可作為數據中心,5 個 bit 作為機器 ID),12bit 作為毫秒內的流水號(意味著每個節點在每毫秒可以產生 4096 個 ID),最后還有一個符號位,永遠是 0。snowflake 算法可以根據自身業務的需求進行一定的調整。比如估算未來的數據中心個數,每個數據中心內的機器數,以及統一毫秒內的并發數來調整在算法中所需要的 bit 數。snowflake 算法的優勢是穩定性高,不依賴于數據庫等第三方系統;使用靈活方便,可以根據業務需求的特性來調整算法中的 bit 位;單機上 ID 單調自增,毫秒數在高位,自增序列在低位,整個 ID 是趨勢遞增的。而其也存在一定的缺陷,包括強依賴機器時鐘,如果機器上時鐘回撥,會導致發號重復或者服務處于不可用狀態;ID 可能不是全局遞增,雖然 ID 在單機上是遞增的,但是由于涉及到分布式環境下的每個機器節點上的時鐘,可能會出現不是全局遞增的場景。
#pragma once#include <chrono>
#include <mutex>
#include <stdexcept>class Snowflake
{public:Snowflake(uint64_t datacenter_id, uint64_t machine_id) : datacenter_id_(datacenter_id), machine_id_(machine_id){if (datacenter_id > kMaxDatacenterId || machine_id > kMaxMachineId){throw std::invalid_argument("Datacenter ID or Machine ID exceeds maximum value");}}uint64_t Generate(){std::lock_guard<std::mutex> lock(mutex_);uint64_t current_timestamp = GetCurrentTimestamp();if (current_timestamp < last_timestamp_){throw std::runtime_error("Clock moved backwards. Refusing to generate ID.");}if (current_timestamp == last_timestamp_){sequence_ = (sequence_ + 1) & kMaxSequence;if (sequence_ == 0){current_timestamp = WaitNextMillis(current_timestamp);}}else{sequence_ = 0;}last_timestamp_ = current_timestamp;return ((current_timestamp - kEpoch) << kTimestampShift) | (datacenter_id_ << kDatacenterIdShift) |(machine_id_ << kMachineIdShift) | sequence_;}private:uint64_t GetCurrentTimestamp() const{return std::chrono::duration_cast<std::chrono::milliseconds>(std::chrono::system_clock::now().time_since_epoch()).count();}uint64_t WaitNextMillis(uint64_t last_timestamp) const{uint64_t timestamp = GetCurrentTimestamp();while (timestamp <= last_timestamp){timestamp = GetCurrentTimestamp();}return timestamp;}private:uint64_t datacenter_id_; // 數據中心IDuint64_t machine_id_; // 機器IDuint64_t sequence_ = 0; // 序列號uint64_t last_timestamp_ = 0; // 上次生成ID的時間戳// 配置參數static constexpr uint64_t kSequenceBits = 12; // 序列號占用位數static constexpr uint64_t kMachineIdBits = 5; // 機器ID占用位數static constexpr uint64_t kDatacenterIdBits = 5; // 數據中心ID占用位數// 最大值計算static constexpr uint64_t kMaxSequence = (1ULL << kSequenceBits) - 1;static constexpr uint64_t kMaxMachineId = (1ULL << kMachineIdBits) - 1;static constexpr uint64_t kMaxDatacenterId = (1ULL << kDatacenterIdBits) - 1;// 位移量static constexpr uint64_t kMachineIdShift = kSequenceBits; // 機器ID左移位數static constexpr uint64_t kDatacenterIdShift = kSequenceBits + kMachineIdBits; // 數據中心ID左移位數static constexpr uint64_t kTimestampShift = kSequenceBits + kMachineIdBits + kDatacenterIdBits; // 時間戳左移位數// 起始時間(2020-01-01 00:00:0 UTC)static constexpr uint64_t kEpoch = 1577836800000ULL;std::mutex mutex_;
};
使用示例:
#include <iostream>#include "Snowflake.h"int main()
{try{Snowflake snowflake(1, 1); // 數據中心ID=1,機器ID=1for (int i = 0; i < 10; ++i){std::cout << snowflake.Generate() << std::endl;}}catch (const std::exception& e){std::cerr << "Error: " << e.what() << std::endl;}return 0;
}