当前位置: 首页 > article >正文

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

雪花ID是一个依赖时间戳根据算法生成的一个Int64的数字ID,一般用来做主键或者订单号等。以下是一个用C#写的雪花ID的简单实现方法

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}");
        }
    }
}

调用方式

 //获取单例实例
 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();

http://www.kler.cn/a/601145.html

相关文章:

  • Apache Tomcat RCE漏洞(CVE-2025-24813)
  • python 格式化利器
  • 亿级分布式系统架构演进实战(八)- 垂直拆分(领域划分及垂直分库设计)
  • springboot使用netty做TCP客户端
  • Java Collection API增强功能系列之六 改进的 ConcurrentHashMap:归约、搜索、计数与 Set 视图详解
  • [图形学]在半球面上按照微表面模型采样
  • 个人博客系统系统~测试报告
  • 【机器学习】什么是随机森林?
  • PCL 点云多平面探测
  • Vue项目的 Sass 全局基础样式格式化方案,包含常见元素的样式重置
  • zynq7020 最小ps环境速通
  • 数据库三级填空+应用题(1)
  • S32K144外设实验(七):FTM输出多路互补带死区PWM
  • 简洁、实用、无插件和更安全为特点的WordPress主题
  • PDF与Markdown的量子纠缠:一场由VLM导演的文档界奇幻秀
  • Android设计模式之工厂方法模式
  • 西门子 CPU 1513-1 PN TCP Server 接收字符串前多了一个问号
  • Chrome(Google) 浏览器安装Vue2、Vue3 Devtools插件方法
  • 前端解决方案:实现网页截图并导出PDF功能
  • STC32单片机驱动UC1705X点阵屏调试VLCD没升压显示拖影