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);
        }
  • 1
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
下面是一个简单的 C# 实现雪花算法的代码: ```csharp public class SnowflakeIdGenerator { private const long Twepoch = 1288834974657L; 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 const int WorkerIdShift = SequenceBits; private const int DatacenterIdShift = SequenceBits + WorkerIdBits; private const int TimestampLeftShift = SequenceBits + WorkerIdBits + DatacenterIdBits; private const long SequenceMask = -1L ^ (-1L << SequenceBits); private readonly object _lock = new object(); private long _lastTimestamp = -1L; private long _sequence = 0L; public SnowflakeIdGenerator(long workerId, long datacenterId) { if (workerId > MaxWorkerId || workerId < 0) { throw new ArgumentException($"worker Id can't be greater than {MaxWorkerId} or less than 0"); } if (datacenterId > MaxDatacenterId || datacenterId < 0) { throw new ArgumentException($"datacenter Id can't be greater than {MaxDatacenterId} or less than 0"); } WorkerId = workerId; DatacenterId = datacenterId; } public long WorkerId { get; } public long DatacenterId { get; } public long NextId() { lock (_lock) { var timestamp = TimeGen(); if (timestamp < _lastTimestamp) { throw new InvalidOperationException($"Clock moved backwards. Refusing to generate id for {_lastTimestamp - timestamp} milliseconds"); } if (_lastTimestamp == timestamp) { _sequence = (_sequence + 1) & SequenceMask; if (_sequence == 0) { timestamp = TilNextMillis(_lastTimestamp); } } else { _sequence = 0; } _lastTimestamp = timestamp; return ((timestamp - Twepoch) << TimestampLeftShift) | (DatacenterId << DatacenterIdShift) | (WorkerId << WorkerIdShift) | _sequence; } } private long TilNextMillis(long lastTimestamp) { var timestamp = TimeGen(); while (timestamp <= lastTimestamp) { timestamp = TimeGen(); } return timestamp; } private static long TimeGen() { return (long)(DateTime.UtcNow - new DateTime(1970, 1, 1, 0, 0, 0, DateTimeKind.Utc)).TotalMilliseconds; } } ``` 使用示例: ```csharp var generator = new SnowflakeIdGenerator(workerId: 1, datacenterId: 1); var id = generator.NextId(); Console.WriteLine(id); ``` 其中,`workerId` 和 `datacenterId` 分别表示工作机器 ID 和数据中心 ID,需要根据具体情况进行配置。`NextId` 方法用于生成下一个雪花算法的 ID

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值