Snowflake 算法的实现
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_; // 数据中心ID
uint64_t machine_id_; // 机器ID
uint64_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=1
for (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;
}