雪花ID是一个依赖时间戳根据算法生成的一个Int64的数字ID,一般用来做主键或者订单号等。以下是一个用C#写的雪花ID的简单实现方法
csharp
using System;
using System.Collections.Concurrent;
using System.Diagnostics;
public class SnowflakeIdGenerator
{
// 配置常量
private const int SignBits = 1;// 符号位
private const int TimestampBits = 41;// 时间戳位,最大值2^41-1=2,147,483,647ms,69年
private const int DataCenterIdBits = 4;// 数据中心ID位,最大值2^4-1=15
private const int MachineIdBits = 6;// 机器ID位,最大值2^6-1=63
private const int DefaultSequenceBits = 12;// 自增序号位,最大值2^12-1=4095
private const int ProcessIdBits = 4;// 进程ID位,最大值2^4-1=15
private const int SequenceBitsWithProcess = 8;//如果使用进程ID,则为8位,否则为12位,最大值2^8-1=255
// 位移计算常量
private const int TimestampShift = 64 - SignBits - TimestampBits;
private const int DataCenterShift = TimestampShift - DataCenterIdBits;
private const int MachineShift = DataCenterShift - MachineIdBits;
private const int ProcessShift = SequenceBitsWithProcess;
// 基准时间
private static readonly DateTime Epoch = new(2025, 3, 1, 0, 0, 0, DateTimeKind.Utc);
private const long MaxTimestamp = (1L << TimestampBits) - 1;
// 单例存储
private static readonly ConcurrentDictionary<string, SnowflakeIdGenerator> _instances = new();
// 各ID最大值
private readonly long _maxDataCenterId = (1 << DataCenterIdBits) - 1;
private readonly long _maxMachineId = (1 << MachineIdBits) - 1;
private readonly long _maxProcessId = (1 << ProcessIdBits) - 1;
private readonly long _maxSequence;
// 状态控制
private readonly object _lock = new object();
private long _lastTimestamp = -1L;
private int _sequence = 0;
// 节点信息
public int DataCenterId { get; }// 数据中心ID
public int MachineId { get; }// 机器ID
public int ProcessId { get; }// 进程ID
public bool UseProcessId { get; }// 是否使用进程ID
private SnowflakeIdGenerator(int dataCenterId, int machineId, int processId)
{
// 参数校验逻辑
ValidateId(dataCenterId, (1 << DataCenterIdBits) - 1, nameof(dataCenterId));
ValidateId(machineId, (1 << MachineIdBits) - 1, nameof(machineId));
UseProcessId = processId != 0;
if (UseProcessId)
{
ValidateId(processId, (1 << ProcessIdBits) - 1, nameof(processId));
}
DataCenterId = dataCenterId;
MachineId = machineId;
ProcessId = processId;
}
public static SnowflakeIdGenerator GetInstance(int dataCenterId, int machineId, int processId = 0)
{
var key = $"{dataCenterId}-{machineId}-{processId}";
return _instances.GetOrAdd(key, _ => new SnowflakeIdGenerator(dataCenterId, machineId, processId));
}
/// <summary>
/// 产生下一个ID
/// </summary>
/// <returns></returns>
public long NextId()
{
lock (_lock)
{
var timestamp = GetValidTimestamp();
if (timestamp == _lastTimestamp)
{
_sequence++;
if (_sequence > MaxSequence)
{
timestamp = WaitNextMillis(timestamp);
_sequence = 0;
}
}
else
{
_sequence = 0;
}
_lastTimestamp = timestamp;
return BuildId(timestamp);
}
}
/// <summary>
/// 获取最大序号
/// </summary>
private int MaxSequence => UseProcessId ?
(1 << SequenceBitsWithProcess) - 1 :
(1 << DefaultSequenceBits) - 1;
/// <summary>
/// 拼接生成ID
/// </summary>
/// <param name="timestamp"></param>
/// <returns></returns>
private long BuildId(long timestamp)
{
var id = (timestamp << TimestampShift)
| ((long)DataCenterId << DataCenterShift)
| ((long)MachineId << MachineShift);
if (UseProcessId)
{
return id | ((long)ProcessId << ProcessShift) | (uint)_sequence;
}
return id | (uint)_sequence;
}
/// <summary>
/// 获取有效的时间戳,防止时间回拨或系统时钟溢出
/// </summary>
/// <returns></returns>
/// <exception cref="InvalidOperationException"></exception>
private long GetValidTimestamp()
{
var timestamp = (DateTimeOffset.UtcNow.Ticks - Epoch.Ticks) / TimeSpan.TicksPerMillisecond;
if (timestamp < _lastTimestamp)
{
throw new InvalidOperationException(
$"Clock moved backwards. Refusing to generate ID for {_lastTimestamp - timestamp}ms");
}
if (timestamp > MaxTimestamp)
{
throw new InvalidOperationException(
$"System clock overflow. Timestamp exceeds {TimestampBits} bits.");
}
return timestamp;
}
// 等待下一毫秒,产生新的时间戳
private static long WaitNextMillis(long currentTimestamp)
{
long timestamp;
var spinWait = new SpinWait();
do
{
spinWait.SpinOnce();
timestamp = (DateTimeOffset.UtcNow.Ticks - Epoch.Ticks) / TimeSpan.TicksPerMillisecond;
} while (timestamp <= currentTimestamp);
return timestamp;
}
// 校验ID,必须在0到最大值之间
private static void ValidateId(int value, long maxValue, string paramName)
{
if (value < 0 || value > maxValue)
{
throw new ArgumentOutOfRangeException(paramName,
$"Value must be between 0 and {maxValue}");
}
}
}
调用方式
csharp
//获取单例实例
var generator = SnowflakeIdGenerator.GetInstance(dataCenterId: 1, machineId: 5);//推荐
long id = generator.NextId();
//或者,加入进程ID
var generator = SnowflakeIdGenerator.GetInstance(dataCenterId: 1, machineId: 5, processId: 10);
long id = generator.NextId();