C# 版 雪花ID

Snowflakes ID

“雪花ID”(Snowflake ID),那么它是一种分布式系统中的唯一性标识符,可以保证在分布式环境下生成全局唯一的ID。它是由Twitter公司创造并开源的。

雪花ID由64位构成,以时间戳为高位,后面的位数分别代表数据中心信息、机器标识和序列号等元素,最终通过位运算组合而成。这样生成的ID具有全局唯一性,且在生成过程中天然自带排序能力。

雪花ID广泛应用于分布式系统中。比如,在微服务架构中,每个服务实例都需要一个唯一的ID,用于追踪相关业务操作的日志和流转情况;在分布式消息队列中,消息需要设置唯一ID,避免重复消费或者消息顺序混乱等问题。

总之,雪花ID是分布式系统中非常实用的工具之一,可以很好地解决全局唯一性的问题,进而帮助分布式系统更好地运转和管理。

雪花ID的结构

雪花ID 包含五个部分:

  1. 符号位:1 位,固定为 0。
  2. 时间戳:41 位,记录毫秒级的时间戳。这里需要注意的是,时间戳不是当前时刻的时间戳,而是指距离某个特定时间点(如某一年月日)的时间差值,因此它可以支持未来数十年甚至几百年的使用。
  3. 机器 ID:10 位,用于区分同一数据中心内不同的机器。与数据中心 ID 类似,机器 ID 也需要手动设置,并确保全局唯一。
  4. 序列号:12 位,表示同一毫秒时间戳内生成的不同 ID 序号。如果在同一毫秒内生成的 ID 数量超过了 4096 个,那么序列号会从 0 开始重新计数。

时钟回拨问题

在雪花ID算法中,系统时钟回拨可能会导致生成的ID不唯一或者无效,因此需要进行处理。

具体地,在每次生成ID时,都需要获取当前的系统时间戳,并与上一次生成ID时使用的时间戳进行比较。如果发现当前的时间戳小于等于上一次的时间戳,则说明系统时钟发生了回拨,此时可以抛出异常或者阻塞等待,直到系统时钟追上之前的时间戳。

下面是一个示例代码片段以便更好地理解,展示了一个基本的时钟回拨处理过程:

function nextSnowflakeId() {
  // ...

  var timestamp = getUnixTimestamp();
  if (timestamp < lastTimestamp) { // 发生时钟回拨
    var offset = lastTimestamp - timestamp;
    if (offset <= 5) { // 小于等于5毫秒,可以等待追赶
      delay(offset);
      timestamp = getUnixTimestamp();
    } else { // 大于5毫秒,抛出异常
      throw new Exception("Clock moved backwards");
    }
  }

  // ...
}

function delay(offset) {
  try {
    Thread.sleep(offset);
  } catch (InterruptedException e) {
    // ignore
  }
}

function getUnixTimestamp() {
  return new Date().getTime();
}

iml6yu.Fingerprint

一个对雪花算法封装的类库

引入nuget包(预览版)

初始化

第一个参数是机器号 1 ~ 255

第二个参数是机器组号 1~3

Fingerprint.UseFingerprint(1, 3);

测试用法

 [TestMethod()]
        public void GetIdTest()
        {
            Fingerprint.UseFingerprint(1, 3);

            var ids = new ConcurrentBag<long>();
            Parallel.For(0, 100, index =>
            {
                var id = Fingerprint.GetId();
                Console.WriteLine(id);
                ids.Add(id);
            });

            Assert.AreEqual(100, ids.Count);
        }
<think>嗯,用户想知道如何在C#中实现雪花ID算法,并且需要示例代码和教程。首先,我需要回忆一下雪花算法的基本原理。雪花算法是Twitter开源的一种分布式ID生成算法,生成的ID是64位的长整型,结构通常包括时间戳、数据中心ID、机器ID和序列号。这样可以在分布式系统中生成唯一ID,避免冲突。 接下来,我需要确认用户提供的引用内容,看看有没有可以直接参考的代码或者步骤。根据引用[1],提到了创建含雪花算法的ID生成类,引用[2]、[4]和[5]有示例代码,引用[3]提供了参考链接。需要综合这些信息来构建一个完整的实现步骤。 首先,需要确定各个部分的位数分配。通常时间戳占41位,数据中心ID和机器ID各占5位,序列号12位。这样加起来正好是64位。但不同的实现可能有微调,比如引用[4]中的代码可能用了不同的位数分配,需要仔细核对。 然后,实现时需要处理并发问题,确保在高并发下序列号能正确自增,可能需要用锁或者线程安全的方式。引用[5]提到可能需要部署单独的服务来运行雪花算法,这说明在分布式系统中,每个节点的数据中心ID和机器ID需要唯一配置,不能冲突。 接下来,代码部分。根据引用[2]和[4]的示例,Snowflake类需要构造函数接收workerId和datacenterId,并有一个生成ID的方法。需要注意时间戳的起始点,比如引用中的代码可能使用了自定义的起始时间,如2020年1月1日,这样可以延长算法的可用时间。 还要处理时间回拨的问题,当系统时间被回调时,可能会导致ID重复。通常的处理方法是等待时间追上或者抛出异常,但在简单的实现中可能暂时忽略,需要根据实际需求处理。 然后,生成ID的步骤:获取当前时间戳,检查是否小于上次生成的时间戳,如果是则处理时间回拨;如果是同一毫秒,则递增序列号,如果序列号超出范围则等待下一毫秒;不同毫秒则重置序列号。最后,将各部分移位组合成最终的ID。 测试部分需要生成多个ID并验证是否递增且唯一。引用中的示例代码用了循环生成10个ID,用户可能需要更多的测试用例或者说明如何验证。 最后,注意事项部分需要强调数据中心ID和机器ID的配置,时间同步的重要性,以及可能的扩展方案,如单独部署ID生成服务。引用[5]提到了这一点,所以需要包含进去。 总结步骤:1. 结构解析;2. 类结构设计;3. 核心代码实现;4. 使用示例;5. 注意事项。需要确保代码符合C#语法,使用锁处理线程安全,正确移位和位操作,以及处理时间回拨的基本逻辑。</think>### C# 实现雪花ID算法分步指南 雪花算法通过$时间戳+机器标识+序列号$组合生成64位唯一ID,支持每秒生成数百万个不重复ID[^5]。以下是详细实现步骤: #### 一、ID结构解析(64位) $$ ID = (timestamp \ll 22) | (datacenterId \ll 17) | (workerId \ll 12) | sequence $$ 1. 时间戳:41位(毫秒级,可用69年) 2. 数据中心ID:5位(最大31) 3. 机器ID:5位(最大31) 4. 序列号:12位(每毫秒生成4096个ID) #### 二、类结构实现 ```csharp public class Snowflake { private const long Twepoch = 1577836800000L; // 起始时间戳(2020-01-01) // 位移量配置 private const int WorkerIdBits = 5; private const int DatacenterIdBits = 5; private const int SequenceBits = 12; // 最大值计算 private const long MaxWorkerId = -1L ^ (-1L << WorkerIdBits); private const long MaxDatacenterId = -1L ^ (-1L << DatacenterIdBits); private readonly object _lock = new object(); private long _lastTimestamp = -1L; private long _sequence = 0L; public long WorkerId { get; } public long DatacenterId { get; } public Snowflake(long workerId, long datacenterId) { if (workerId > MaxWorkerId || workerId < 0) throw new ArgumentException($"Worker ID必须介于0和{MaxWorkerId}之间"); if (datacenterId > MaxDatacenterId || datacenterId < 0) throw new ArgumentException($"数据中心ID必须介于0和{MaxDatacenterId}之间"); WorkerId = workerId; DatacenterId = datacenterId; } public long NextId() { lock (_lock) { var timestamp = TimeGen(); // 处理时间回拨 if (timestamp < _lastTimestamp) throw new Exception($"系统时钟回拨{_lastTimestamp - timestamp}毫秒"); // 同一毫秒内生成 if (_lastTimestamp == timestamp) { _sequence = (_sequence + 1) & ((1 << SequenceBits) - 1); if (_sequence == 0) // 当前毫秒序列号用完 timestamp = TilNextMillis(_lastTimestamp); } else // 新毫秒重置序列号 { _sequence = 0; } _lastTimestamp = timestamp; return ((timestamp - Twepoch) << 22) | (DatacenterId << 17) | (WorkerId << 12) | _sequence; } } private long TilNextMillis(long lastTimestamp) { var timestamp = TimeGen(); while (timestamp <= lastTimestamp) timestamp = TimeGen(); return timestamp; } private long TimeGen() => DateTimeOffset.UtcNow.ToUnixTimeMilliseconds(); } ``` #### 三、使用示例 ```csharp // 初始化生成器(参数为机器ID和数据中心ID) var snowflake = new Snowflake(workerId: 1, datacenterId: 1); // 生成10个ID for (int i = 0; i < 10; i++) { long id = snowflake.NextId(); Console.WriteLine($"生成ID: {id} -> 二进制: {Convert.ToString(id, 2)}"); } ``` #### 四、关键注意事项 1. **机器标识分配**:每个节点必须配置唯一的$WorkerId$和$DatacenterId$ 2. **时钟同步**:必须使用NTP保持系统时钟同步,时间回拨超过阈值需人工干预[^3] 3. **性能表现**:单机每秒可生成约$409.6万$个ID(1毫秒/4096个)[^1] 4. **扩展方案**:可部署为独立的ID生成服务,通过REST API提供ID生成接口[^5] #### 五、ID结构验证示例 当生成ID为$6917168173087129600$时: $$ \begin{aligned} 二进制&: 1100000001100000111100001101111000000000000000000000000000000000 \\ 时间戳&: (1100000001100000111100001101111000000000)_2 = 1616838173087 \\ 数据中心&: (00001)_2 = 1 \\ 机器ID&: (00001)_2 = 1 \\ 序列号&: (000000000000)_2 = 0 \\ \end{aligned} $$
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值