Snowflake 是 Twitter 开源的分布式 ID 生成算法,它可以在不依赖数据库的情况下生成全局唯一的 ID。下面是一个简单的 C# 实现:
using System;
public class Snowflake
{
// 基准时间
private static readonly DateTime Epoch = new DateTime(2021, 1, 1, 0, 0, 0, DateTimeKind.Utc);
// 机器 ID 所占位数
private const int WorkerIdBits = 5;
// 数据中心 ID 所占位数
private const int DatacenterIdBits = 5;
// 序列号所占位数
private const int SequenceBits = 12;
// 机器 ID 左移位数
private const int WorkerIdShift = SequenceBits;
// 数据中心 ID 左移位数
private const int DatacenterIdShift = SequenceBits + WorkerIdBits;
// 时间戳左移位数
private const int TimestampLeftShift = SequenceBits + WorkerIdBits + DatacenterIdBits;
// 最大序列号
private const long MaxSequence = (1L << SequenceBits) - 1;
// 机器 ID 和数据中心 ID 的最大值
private const long MaxWorkerId = (1L<< WorkerIdBits) - 1;
private const long MaxDatacenterId = (1L<< DatacenterIdBits) - 1;
// 机器 ID
private readonly long _workerId;
// 数据中心 ID
private readonly long _datacenterId;
// 序列号
private long _sequence;
// 上次生成 ID 的时间戳
private long _lastTimestamp;
public Snowflake(long workerId, long datacenterId, long sequence = 0L)
{
if (workerId < 0 || workerId > MaxWorkerId)
throw new ArgumentException($"Worker Id must be between 0 and {MaxWorkerId}");
if (datacenterId < 0 || datacenterId > MaxDatacenterId)
throw new ArgumentException($"Datacenter Id must be between 0 and {MaxDatacenterId}");
_workerId = workerId;
_datacenterId = datacenterId;
_sequence = sequence;
}
public long NextId()
{
lock (this)
{
var timestamp = GetCurrentTimestamp();
if (timestamp < _lastTimestamp)
throw new Exception("Invalid system clock");
if (_lastTimestamp == timestamp)
{
_sequence = (_sequence + 1) & MaxSequence;
if (_sequence == 0)
timestamp = WaitNextMillisecond(_lastTimestamp);
}
else
{
_sequence = 0;
}
_lastTimestamp = timestamp;
return ((timestamp - Epoch.Ticks)<< TimestampLeftShift) |
(_datacenterId<< DatacenterIdShift) |
(_workerId<< WorkerIdShift) |
_sequence;
}
}
private long GetCurrentTimestamp()
{
return (DateTime.UtcNow - Epoch).Ticks / 10000;
}
private long WaitNextMillisecond(long lastTimestamp)
{
var timestamp = GetCurrentTimestamp();
while (timestamp <= lastTimestamp)
timestamp = GetCurrentTimestamp();
return timestamp;
}
}
使用方法:
var snowflake = new Snowflake(1, 1);
var id = snowflake.NextId();
Console.WriteLine(id);
这个实现中,我们使用了一个基准时间(Epoch),机器 ID(workerId),数据中心 ID(datacenterId)和序列号(sequence)来生成全局唯一的 ID。你需要为每个工作节点分配一个唯一的机器 ID 和数据中心 ID。