雪花算法是Twitter开源的分布式ID生成算法,核心思想是将一个64位的ID拆分为不同部分,分别存储时间戳、机器标识和序列号,以此保证在分布式环境下生成的ID全局唯一且趋势递增。在C#中实现该算法,需要先明确ID的位划分规则,再结合线程安全、时钟回拨等场景做适配处理。

雪花算法ID结构说明
标准的雪花算法64位ID划分如下:
- 最高位(1位):固定为0,保证ID为正数
- 时间戳部分(41位):记录当前时间与自定义起始时间的差值,单位通常为毫秒,可支持约69年的时间范围
- 机器ID部分(10位):分为5位数据中心ID和5位机器ID,最多支持1024个节点
- 序列号部分(12位):同一毫秒内自增的序列,最多支持每毫秒生成4096个ID
C#实现核心逻辑
实现时需要注意几个关键点:一是使用lock关键字保证多线程下的序列号自增和ID生成的线程安全;二是处理时钟回拨场景,避免生成重复ID;三是合理设置起始时间戳,减少时间戳差值溢出的风险。
完整实现代码
using System;
public class SnowflakeIdGenerator
{
// 起始时间戳(可自定义,建议设置为项目上线时间的时间戳毫秒值)
private static readonly long START_TIMESTAMP = 1609459200000L; // 2021-01-01 00:00:00的毫秒时间戳
// 各部分占用的位数
private static readonly int SEQUENCE_BITS = 12; // 序列号位数
private static readonly int MACHINE_ID_BITS = 5; // 机器ID位数
private static readonly int DATA_CENTER_ID_BITS = 5; // 数据中心ID位数
// 最大取值计算
private static readonly long MAX_MACHINE_ID = -1L ^ (-1L << MACHINE_ID_BITS);
private static readonly long MAX_DATA_CENTER_ID = -1L ^ (-1L << DATA_CENTER_ID_BITS);
private static readonly long MAX_SEQUENCE = -1L ^ (-1L << SEQUENCE_BITS);
// 各部分左移位数
private static readonly int MACHINE_ID_SHIFT = SEQUENCE_BITS;
private static readonly int DATA_CENTER_ID_SHIFT = SEQUENCE_BITS + MACHINE_ID_BITS;
private static readonly int TIMESTAMP_SHIFT = SEQUENCE_BITS + MACHINE_ID_BITS + DATA_CENTER_ID_BITS;
// 实例变量
private readonly long _dataCenterId;
private readonly long _machineId;
private long _sequence = 0L; // 当前毫秒内的序列号
private long _lastTimestamp = -1L; // 上一次生成ID的时间戳
private readonly object _lockObj = new object();
/// <summary>
/// 初始化雪花ID生成器
/// </summary>
/// <param name="dataCenterId">数据中心ID,范围0~31</param>
/// <param name="machineId">机器ID,范围0~31</param>
public SnowflakeIdGenerator(long dataCenterId, long machineId)
{
if (dataCenterId > MAX_DATA_CENTER_ID || dataCenterId < 0)
{
throw new ArgumentException($"数据中心ID必须在0到{MAX_DATA_CENTER_ID}之间");
}
if (machineId > MAX_MACHINE_ID || machineId < 0)
{
throw new ArgumentException($"机器ID必须在0到{MAX_MACHINE_ID}之间");
}
_dataCenterId = dataCenterId;
_machineId = machineId;
}
/// <summary>
/// 生成下一个唯一ID
/// </summary>
public long NextId()
{
lock (_lockObj)
{
long currentTimestamp = GetCurrentTimestamp();
// 处理时钟回拨
if (currentTimestamp < _lastTimestamp)
{
throw new InvalidOperationException($"时钟回拨,拒绝生成ID,回拨时间差:{_lastTimestamp - currentTimestamp}毫秒");
}
// 同一毫秒内
if (currentTimestamp == _lastTimestamp)
{
_sequence = (_sequence + 1) & MAX_SEQUENCE;
// 序列号溢出,等待下一毫秒
if (_sequence == 0)
{
currentTimestamp = WaitNextMillis(_lastTimestamp);
}
}
else
{
// 不同毫秒内,序列号重置为0
_sequence = 0;
}
_lastTimestamp = currentTimestamp;
// 组装ID
return ((currentTimestamp - START_TIMESTAMP) << TIMESTAMP_SHIFT)
| (_dataCenterId << DATA_CENTER_ID_SHIFT)
| (_machineId << MACHINE_ID_SHIFT)
| _sequence;
}
}
/// <summary>
/// 获取当前时间戳(毫秒)
/// </summary>
private long GetCurrentTimestamp()
{
return DateTimeOffset.UtcNow.ToUnixTimeMilliseconds();
}
/// <summary>
/// 等待直到下一毫秒
/// </summary>
private long WaitNextMillis(long lastTimestamp)
{
long timestamp = GetCurrentTimestamp();
while (timestamp <= lastTimestamp)
{
timestamp = GetCurrentTimestamp();
}
return timestamp;
}
}
使用示例
在业务代码中可以直接实例化生成器并调用方法获取ID:
class Program
{
static void Main(string[] args)
{
// 初始化生成器,数据中心ID为1,机器ID为1
SnowflakeIdGenerator generator = new SnowflakeIdGenerator(1, 1);
// 生成10个ID并打印
for (int i = 0; i < 10; i++)
{
long id = generator.NextId();
Console.WriteLine($"生成的Snowflake ID:{id}");
}
}
}
注意事项
- 起始时间戳建议设置为项目正式上线的时间,避免时间戳差值过早达到上限
- 如果部署环境存在时钟回拨问题,可以扩展代码逻辑,比如记录回拨时间并等待恢复,或者临时切换备用生成策略
- 机器ID和数据中心ID需要保证在分布式集群中唯一,可以通过配置文件、环境变量或者注册中心分配
- 该实现是单实例的,如果需要在多实例场景下使用,只需要保证不同实例的机器ID和数据中心ID不重复即可
C#Snowflake_IDID生成器分布式ID修改时间:2026-07-06 18:45:26