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

news2025/4/1 9:34:41

雪花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.coloradmin.cn/o/2324185.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

【实战ES】实战 Elasticsearch:快速上手与深度实践-2.2.1 Bulk API的正确使用与错误处理

&#x1f449; 点击关注不迷路 &#x1f449; 点击关注不迷路 &#x1f449; 点击关注不迷路 文章大纲 Elasticsearch Bulk API 深度实践&#xff1a;性能调优与容错设计1. Bulk API 核心机制解析1.1 批量写入原理剖析1.1.1 各阶段性能瓶颈 2. 高性能批量写入实践2.1 客户端最佳…

鸿蒙Flutter开发故事:不,你不需要鸿蒙化

在华为牵头下&#xff0c;Flutter 鸿蒙化如火如荼进行&#xff0c;当第一次看到一份上百个插件的Excel 列表时&#xff0c;我也感到震惊&#xff0c;排名前 100 的插件赫然在列&#xff0c;这无疑是一次大规模的军团作战。 然后&#xff0c;参战团队鱼龙混杂&#xff0c;难免有…

中间件框架漏洞攻略

中间件&#xff08;英语&#xff1a;Middleware&#xff09;是提供系统软件和应⽤软件之间连接的软件&#xff0c;以便于软件各部件之间的沟通。 中间件处在操作系统和更⾼⼀级应⽤程序之间。他充当的功能是&#xff1a;将应⽤程序运⾏环境与操作系统隔离&#xff0c;从⽽实…

第21周:RestNet-50算法实践

目录 前言 理论知识 1.CNN算法发展 2.-残差网络的由来 一、导入数据 二、数据处理 四、编译 五、模型评估 六、总结 前言 &#x1f368; 本文为&#x1f517;365天深度学习训练营中的学习记录博客&#x1f356; 原作者&#xff1a;K同学啊 理论知识 1.CNN算法发展 该图列举出…

构建大语言模型应用:数据准备(第二部分)

本专栏通过检索增强生成&#xff08;RAG&#xff09;应用的视角来学习大语言模型&#xff08;LLM&#xff09;。 本系列文章 简介数据准备&#xff08;本文&#xff09;句子转换器向量数据库搜索与检索大语言模型开源检索增强生成评估大语言模型服务高级检索增强生成 RAG 如上…

AI-Sphere-Butler之Ubuntu服务器如何部署Nginx代理,并将HTTP升级成HTTPS,用于移动设备访问

环境&#xff1a; AI-Sphere-Butler WSL2 Ubuntu22.04 Nginx 问题描述&#xff1a; AI-Sphere-Butler之Ubuntu服务器如何部署Nginx代理&#xff0c;并将HTTP升级成HTTPS&#xff0c;用于移动设备访问 解决方案&#xff1a; 一、生成加密证书 1.配置OpenSSL生成本地不加…

飞致云荣获“Alibaba Cloud Linux最佳AI镜像服务商”称号

2025年3月24日&#xff0c;阿里云云市场联合龙蜥社区发布“2024年度Alibaba Cloud Linux最佳AI镜像服务商”评选结果。 经过主办方的严格考量&#xff0c;飞致云&#xff08;即杭州飞致云信息科技有限公司&#xff09;凭借旗下MaxKB开源知识库问答系统、1Panel开源面板、Halo开…

Django项目之订单管理part6(message组件和组合搜索组件)

一.前言 我们前面讲的差不多了&#xff0c;接着上节课讲&#xff0c;今天要来做一个撤单要求&#xff0c;我们可以用ajax请求&#xff0c;但是我这里介绍最后一个知识点&#xff0c;message组件&#xff0c;但是我会把两种方式都讲出来的&#xff0c;讲完这个就开始讲我们最重…

Taro创建微信小程序项目 第一步搭建项目

1.node: 2.第一步&#xff1a; 安装taro npm install -g tarojs/cli 3.创建文件夹wxxcx, 创建demos的文件夹的项目&#xff08;demos项目名称&#xff09; taro init demos 出现以下信息&#xff1a;可以根据自己的需求选择 出现安装项目依赖失败不要紧 4.进入demos文件夹…

S32K144外设实验(六):FTM输出单路PWM

文章目录 1. 概述1.1 时钟系统1.2 实验目的2. 代码的配置2.1 时钟配置2.2 FTM模块配置2.3 输出引脚配置2.4 API函数调用1. 概述 1.1 时钟系统 FTM的CPU接口时钟为SYS_CLK,在RUN模式下最高80MHz。模块的时钟结构如下图所示。 从上图中可以看出,FTM模块的功能时钟为SYS_CLK,…

地下管线三维建模软件工具MagicPipe3D V3.6.1

经纬管网建模系统MagicPipe3D&#xff0c;基于二维矢量管线管点数据本地离线参数化构建地下管网三维模型&#xff08;包括管道、接头、附属设施等&#xff09;&#xff0c;输出标准3DTiles、Obj模型等格式&#xff0c;支持Cesium、Unreal、Unity、Osg等引擎加载进行三维可视化、…

iOS自定义collection view的page size(width/height)分页效果

前言 想必大家工作中或多或少会遇到下图样式的UI需求吧 像这种cell长度不固定&#xff0c;并且还能实现的分页效果UI还是很常见的 实现 我们这里实现主要采用collection view&#xff0c;实现的方式是自定义一个UICollectionViewFlowLayout的子类&#xff0c;在这个类里对…

以科技赋能,炫我云渲染受邀参加中关村文化科技融合影视精品创作研讨会!

在文化与科技深度融合的时代浪潮下&#xff0c;影视创作行业经历着前所未有的变革。影视创作行业发展态势迅猛&#xff0c; 同时也面临着诸多挑战。为促进影视创作行业的创新发展&#xff0c;加强业内交流与合作&#xff0c; 3月25日下午&#xff0c;海淀区文化创意产业协会举办…

华为、浪潮、华三链路聚合概述

1、华为 链路聚合可以提高链路带宽和链路冗余性。有三种类型&#xff0c;分别是手工链路聚合&#xff0c;静态lacp链路聚合&#xff0c;动态lacp链路聚合。 手工链路模式&#xff1a;也称负载分担模式&#xff0c;需手动指定链路&#xff0c;各链路之间平均分担流量。静态LAC…

【go微服务】Golang微服务之基--rpc的实现原理以及应用实战

✨✨ 欢迎大家来到景天科技苑✨✨ &#x1f388;&#x1f388; 养成好习惯&#xff0c;先赞后看哦~&#x1f388;&#x1f388; &#x1f3c6; 作者简介&#xff1a;景天科技苑 &#x1f3c6;《头衔》&#xff1a;大厂架构师&#xff0c;华为云开发者社区专家博主&#xff0c;…

Redis的List类型

Redis的List类型 一.List类型简单介绍 二.List的常用命令1.LPUSH2.LRANGE3.LPUSHX4.RPUSH5.RPUSHX6.LPOP7.RPOP8.LINDEX9.LINSERT10.LLEN11.LREM12.LTRIM13.LSET 三.阻塞命令1.BRPOP&#xff08;i&#xff09;针对不是空的列表进行操作&#xff1a;&#xff08;ii&#xff09;针…

【C语言】分支与循环(下)

前言&#xff1a;小飞在&#xff08;上&#xff09;篇总结了分支结构的内容&#xff0c;本文接上&#xff0c;总结循环结构的知识。 看完觉得有帮助的话记得点赞收藏加关注哦~ 目录 一、while循环 二、for循环 三、do-while循环 四、循环中的break和continue 五、循环结构…

SpringBoot集成腾讯云OCR实现身份证识别

OCR身份证识别 官网地址&#xff1a;https://cloud.tencent.com/document/product/866/33524 身份信息认证&#xff08;二要素核验&#xff09; 官网地址&#xff1a;https://cloud.tencent.com/document/product/1007/33188 代码实现 引入依赖 <dependency><…

【C++数据库】SQLite3数据库连接与操作

注意:本文代码均为C++20标准下实现 一、SQLite3库安装 1.1 安装库文件 【工具】跨平台C++包管理利器vcpkg完全指南 vcpkg install sqlite3# 集成至系统目录,之前执行过此命令的无需再次执行 vcpkg integrate install1.2 验证代码 在VS2022中新建控制台项目,测试代码如下…

如何在根据名称或id找到json里的节点以及对应的所有的父节点?

函数如下&#xff1a; 数据如下&#xff1a; [{ "name": "数据看板", "id": "data", "pageName": "tableeauData", "list": [] }, { "name": "审计模块", "id": &quo…