一个简单的用C#实现的分布式雪花ID算法

雪花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();
相关推荐
ElseWhereR3 小时前
机器人能否回到原点 - 简单
c++·算法·leetcode
my_realmy3 小时前
蓝桥杯真题_小蓝和小桥的讨论
java·python·算法·职场和发展·蓝桥杯·intellij-idea
泛舟起晶浪3 小时前
海底高铁--差分
数据结构·c++·算法
带娃的IT创业者3 小时前
《Python实战进阶》No37: 强化学习入门:Q-Learning 与 DQN-加餐版1 Q-Learning算法可视化
python·算法·pygame
hhcyyds14 小时前
【C#】变量和常量
c#
ゞ 正在缓冲99%…4 小时前
leetcode73.矩阵置零
java·数据结构·算法·矩阵
qq_340474024 小时前
5.1 WPF路由事件以及文本样式
前端·c#·wpf
AskHarries5 小时前
Dify 服务器部署指南
后端·算法
QuantumStack5 小时前
【C++ 真题】P1109 学生分组
java·数据结构·c++·算法
小王C语言5 小时前
【数据结构初阶&&八大排序】---冒泡、选择、插入、希尔、堆排、快排、归并、计数
数据结构·算法·排序算法