赵永田 發表於 2026-1-5 08:33:19

C# .NET实现雪花ID生成器的具体方案

<div id="navCategory"><h5 class="catalogue">目录</h5><ul class="first_class_ul"><li>引言</li><li>一、雪花ID核心结构(64位long型)</li><li>二、.NET实现雪花ID生成器</li><li>三、使用示例</li><li>四、关键说明</li><li>总结</li></ul></div><p class="maodian"></p><h2>引言</h2>
<p>你希望在.NET中实现一个完整、线程安全且能处理时钟回拨问题的雪花ID(Snowflake)算法,用于分库分表场景下生成全局唯一的分布式ID,避免不同库/表的主键重复。</p>
<p>雪花ID是Twitter开源的分布式ID生成算法,核心是将64位长整型ID按规则拆分,保证全局唯一且趋势递增。下面先讲解核心结构,再给出可直接使用的.NET实现代码。</p>
<p class="maodian"></p><h2>一、雪花ID核心结构(64位long型)</h2>
<table><thead><tr><th>位段</th><th>长度</th><th>作用</th></tr></thead><tbody><tr><td>符号位</td><td>1位</td><td>固定为0(保证ID为正数)</td></tr><tr><td>时间戳位</td><td>41位</td><td>记录生成ID的毫秒级时间戳(相对于一个起始时间),可支持约69年(2^41-1)</td></tr><tr><td>机器/节点位</td><td>10位</td><td>分为5位数据中心ID + 5位机器ID,最多支持1024个节点部署</td></tr><tr><td>序列号位</td><td>12位</td><td>同一毫秒内的自增序列,每毫秒最多生成4096个ID(2^12=4096)</td></tr></tbody></table>
<p class="maodian"></p><h2>二、.NET实现雪花ID生成器</h2>
<p>以下实现包含<strong>线程安全</strong>、<strong>时钟回拨检测</strong>、<strong>参数合法性校验</strong>,可直接在生产环境使用:</p>
<div class="jb51code"><pre class="brush:csharp;">using System;
using System.Threading;

/// &lt;summary&gt;
/// 雪花ID生成器(线程安全、支持时钟回拨检测)
/// &lt;/summary&gt;
public class SnowflakeIdGenerator
{
    #region 常量定义
    // 起始时间戳(UTC时间:2024-01-01 00:00:00),可自行调整,建议使用项目上线前的时间
    private const long Epoch = 1704067200000L;

    // 各部分的位长度
    private const int TimestampBits = 41; // 时间戳位长度
    private const int DataCenterIdBits = 5; // 数据中心ID位长度
    private const int MachineIdBits = 5; // 机器ID位长度
    private const int SequenceBits = 12; // 序列号位长度

    // 最大值(位运算计算)
    private const long MaxDataCenterId = (1L &lt;&lt; DataCenterIdBits) - 1; // 31
    private const long MaxMachineId = (1L &lt;&lt; MachineIdBits) - 1; // 31
    private const long MaxSequence = (1L &lt;&lt; SequenceBits) - 1; // 4095

    // 位移量
    private const int MachineIdShift = SequenceBits; // 12
    private const int DataCenterIdShift = SequenceBits + MachineIdBits; // 17
    private const int TimestampShift = SequenceBits + MachineIdBits + DataCenterIdBits; // 22
    #endregion

    #region 私有字段
    private readonly long _dataCenterId; // 数据中心ID
    private readonly long _machineId; // 机器ID
    private long _lastTimestamp = -1L; // 上一次生成ID的时间戳
    private long _sequence = 0L; // 当前毫秒内的序列号
    private readonly object _lockObj = new(); // 锁对象(保证线程安全)
    #endregion

    /// &lt;summary&gt;
    /// 构造函数
    /// &lt;/summary&gt;
    /// &lt;param name="dataCenterId"&gt;数据中心ID(0-31)&lt;/param&gt;
    /// &lt;param name="machineId"&gt;机器ID(0-31)&lt;/param&gt;
    /// &lt;exception cref="ArgumentException"&gt;参数超出范围时抛出&lt;/exception&gt;
    public SnowflakeIdGenerator(long dataCenterId, long machineId)
    {
      // 校验参数合法性
      if (dataCenterId &lt; 0 || dataCenterId &gt; MaxDataCenterId)
      {
            throw new ArgumentException($"数据中心ID必须在0-{MaxDataCenterId}之间", nameof(dataCenterId));
      }
      if (machineId &lt; 0 || machineId &gt; MaxMachineId)
      {
            throw new ArgumentException($"机器ID必须在0-{MaxMachineId}之间", nameof(machineId));
      }

      _dataCenterId = dataCenterId;
      _machineId = machineId;
    }

    /// &lt;summary&gt;
    /// 生成下一个雪花ID
    /// &lt;/summary&gt;
    /// &lt;returns&gt;全局唯一的雪花ID&lt;/returns&gt;
    /// &lt;exception cref="Exception"&gt;时钟回拨超出容忍范围时抛出&lt;/exception&gt;
    public long NextId()
    {
      lock (_lockObj) // 加锁保证多线程安全
      {
            // 1. 获取当前时间戳(毫秒)
            long currentTimestamp = GetCurrentTimestamp();

            // 2. 处理时钟回拨问题
            if (currentTimestamp &lt; _lastTimestamp)
            {
                long timeDiff = _lastTimestamp - currentTimestamp;
                // 容忍500ms内的时钟回拨(等待直到时间追上)
                if (timeDiff &lt;= 500)
                {
                  // 自旋等待,直到时间戳追上上次的时间戳
                  while (currentTimestamp &lt; _lastTimestamp)
                  {
                        currentTimestamp = GetCurrentTimestamp();
                  }
                }
                else
                {
                  // 超出容忍范围,抛出异常(避免ID重复)
                  throw new Exception($"时钟回拨异常!当前时间戳:{currentTimestamp},上次时间戳:{_lastTimestamp},差值:{timeDiff}ms");
                }
            }

            // 3. 处理同一毫秒内的序列号
            if (currentTimestamp == _lastTimestamp)
            {
                // 序列号自增,超过最大值则等待下一毫秒
                _sequence = (_sequence + 1) &amp; MaxSequence;
                if (_sequence == 0)
                {
                  // 自旋等待下一毫秒
                  while (currentTimestamp &lt;= _lastTimestamp)
                  {
                        currentTimestamp = GetCurrentTimestamp();
                  }
                }
            }
            else
            {
                // 新的毫秒,序列号重置为0
                _sequence = 0;
            }

            // 4. 更新上次时间戳
            _lastTimestamp = currentTimestamp;

            // 5. 拼接各部分生成最终ID
            return ((currentTimestamp - Epoch) &lt;&lt; TimestampShift) // 时间戳部分
                   | (_dataCenterId &lt;&lt; DataCenterIdShift) // 数据中心ID部分
                   | (_machineId &lt;&lt; MachineIdShift) // 机器ID部分
                   | _sequence; // 序列号部分
      }
    }

    /// &lt;summary&gt;
    /// 获取当前UTC时间戳(毫秒)
    /// &lt;/summary&gt;
    /// &lt;returns&gt;毫秒级时间戳&lt;/returns&gt;
    private long GetCurrentTimestamp()
    {
      return (long)(DateTime.UtcNow - new DateTime(1970, 1, 1, 0, 0, 0, DateTimeKind.Utc)).TotalMilliseconds;
    }

    #region 便捷方法(可选)
    /// &lt;summary&gt;
    /// 生成雪花ID的字符串形式
    /// &lt;/summary&gt;
    /// &lt;returns&gt;ID字符串&lt;/returns&gt;
    public string NextIdString()
    {
      return NextId().ToString();
    }

    /// &lt;summary&gt;
    /// 解析雪花ID,返回各部分信息(用于调试)
    /// &lt;/summary&gt;
    /// &lt;param name="id"&gt;雪花ID&lt;/param&gt;
    /// &lt;returns&gt;解析后的信息&lt;/returns&gt;
    public (DateTime GenerateTime, long DataCenterId, long MachineId, long Sequence) ParseId(long id)
    {
      long timestamp = (id &gt;&gt; TimestampShift) + Epoch;
      DateTime generateTime = new DateTime(1970, 1, 1, 0, 0, 0, DateTimeKind.Utc).AddMilliseconds(timestamp);
      long dataCenterId = (id &gt;&gt; DataCenterIdShift) &amp; MaxDataCenterId;
      long machineId = (id &gt;&gt; MachineIdShift) &amp; MaxMachineId;
      long sequence = id &amp; MaxSequence;

      return (generateTime.ToLocalTime(), dataCenterId, machineId, sequence);
    }
    #endregion
}

</pre></div>
<p class="maodian"></p><h2>三、使用示例</h2>
<div class="jb51code"><pre class="brush:csharp;">class Program
{
    static void Main(string[] args)
    {
      // 初始化生成器(数据中心ID=1,机器ID=1,根据实际部署环境调整)
      var idGenerator = new SnowflakeIdGenerator(1, 1);

      // 单线程生成ID
      long id1 = idGenerator.NextId();
      Console.WriteLine($"生成的雪花ID:{id1}");

      // 解析ID(调试用)
      var parseResult = idGenerator.ParseId(id1);
      Console.WriteLine($"生成时间:{parseResult.GenerateTime}");
      Console.WriteLine($"数据中心ID:{parseResult.DataCenterId}");
      Console.WriteLine($"机器ID:{parseResult.MachineId}");
      Console.WriteLine($"序列号:{parseResult.Sequence}");

      // 多线程测试(验证线程安全)
      Parallel.For(0, 1000, i =&gt;
      {
            long id = idGenerator.NextId();
            Console.WriteLine($"线程{Thread.CurrentThread.ManagedThreadId}生成ID:{id}");
      });
    }
}

</pre></div>
<p class="maodian"></p><h2>四、关键说明</h2>
<ol><li><strong>起始时间戳(Epoch)</strong>:建议设置为项目上线前的时间(如示例中的2024-01-01),可延长ID使用周期(默认能用到2093年)。</li><li><strong>机器/数据中心ID</strong>:部署时需保证每个节点的(数据中心ID+机器ID)唯一(比如通过配置文件/环境变量设置),避免不同节点生成重复ID。</li><li><strong>时钟回拨处理</strong>:代码中容忍500ms内的时钟回拨(自旋等待),超出则抛异常,可根据业务调整容忍阈值。</li><li><strong>性能</strong>:单节点每秒可生成约400万+ ID(1ms生成4096个,1秒=1000ms &rarr; 4096*1000=4,096,000),满足绝大多数业务场景。</li></ol>
<p class="maodian"></p><h2>总结</h2>
<ol><li>雪花ID核心是通过<strong>位运算</strong>将时间戳、机器ID、序列号拼接成64位唯一ID,保证全局唯一且趋势递增;</li><li>实现时必须保证<strong>线程安全</strong>(加锁)和<strong>时钟回拨检测</strong>(避免ID重复);</li><li>使用时需保证每个节点的机器/数据中心ID唯一,起始时间戳建议根据项目实际调整。</li></ol>
<p>这个实现可直接集成到你的.NET分库分表项目中,作为分布式主键生成方案。</p>
<p>到此这篇关于C# .NET实现雪花ID生成器的具体方案的文章就介绍到这了,更多相关C# .NET雪花ID生成器内容请搜索琼殿技术社区以前的文章或继续浏览下面的相关文章希望大家以后多多支持琼殿技术社区!</p>
                           
                            <div class="art_xg">
                              <b>您可能感兴趣的文章:</b><ul><li>C#利用雪花算法实现生成ID工具类</li><li>C# Guid长度雪花简单生成器的示例代码</li><li>C#&nbsp;实现雪花算法(Snowflake&nbsp;Algorithm)的实现</li><li>开源一个c# 新的雪花算法</li><li>c# 实现雪花分形的示例</li></ul>
                            </div>

                        </div>
                        <!--endmain-->
頁: [1]
查看完整版本: C# .NET实现雪花ID生成器的具体方案