C#雪花算法生成id

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;

namespace ConsoleApp_SendMsg
{
    /// <summary>
    /// 雪花算法
    /// </summary>
    /// <remarks>
    /// 最高位是符号位,始终为0,不可用。
    /// 41位的时间序列,精确到毫秒级,41位的长度可以使用69年。时间位还有一个很重要的作用是可以根据时间进行排序。
    /// 10位的机器标识,10位的长度最多支持部署1024个节点。
    /// 12位的计数序列号,序列号即一系列的自增id,可以支持同一节点同一毫秒生成多个ID序号,12位的计数序列号支持每个节点每毫秒产生4096个ID序号。
    /// </remarks>
    public class Snowflake
    {
        #region 属性
        // 机器id所占的位数
        private const int workerIdBits = 5;
        // 数据标识id所占的位数
        private const int datacenterIdBits = 5;
        // 支持的最大机器id,结果是31 (这个移位算法可以很快的计算出几位二进制数所能表示的最大十进制数)
        private const long maxWorkerId = -1L ^ (-1L << workerIdBits);
        // 支持的最大数据标识id,结果是31
        private const long maxDatacenterId = -1L ^ (-1L << datacenterIdBits);
        // 序列在id中占的位数
        private const int sequenceBits = 12;
        // 数据标识id向左移17位(12+5)
        private const int datacenterIdShift = sequenceBits + workerIdBits;
        // 机器ID向左移12位
        private const int workerIdShift = sequenceBits;
        // 时间截向左移22位(5+5+12)
        private const int timestampLeftShift = sequenceBits + workerIdBits + datacenterIdBits;
        // 生成序列的掩码,这里为4095 (0b111111111111=0xfff=4095)
        private const long sequenceMask = -1L ^ (-1L << sequenceBits);

        /// <summary>
        /// 数据中心ID(0~31)
        /// </summary>
        public long DatacenterId { get; private set; }
        /// <summary>
        /// 工作机器ID(0~31)
        /// </summary>
        public long WorkerId { get; private set; }
        /// <summary>
        ///  毫秒内序列(0~4095)
        /// </summary>
        public long Sequence { get; private set; }
        /// <summary>
        /// 上次生成ID的时间截
        /// </summary>
        public long LastTimestamp { get; private set; }
        /// <summary>
        /// 开始时间戳。首次使用前设置,否则无效,默认2010-1-1
        /// </summary>
        public DateTime StartTimestamp { get; set; } = new DateTime(2010, 1, 1);

        static object syncRoot = new object();
        static readonly Lazy<Snowflake> snowflake = new(() => new Snowflake(0L, 0L));

        /// <summary>
        /// 默认静态实例,WorkerId=0,DatacenterId=0
        /// </summary>
        public static Snowflake Instance { get; } = snowflake.Value;
        #endregion

        #region 构造函数
        /// <summary>
        /// 雪花ID
        /// </summary>
        /// <param name="workerId">工作机器ID</param>
        /// <param name="datacenterId">数据中心ID</param>
        public Snowflake(long workerId, long datacenterId)
        {
            if (workerId > maxWorkerId || workerId < 0)
                throw new ArgumentOutOfRangeException(nameof(workerId), $"不能大于 {maxWorkerId} 或小于 0");
            if (datacenterId > maxDatacenterId || datacenterId < 0)
                throw new ArgumentOutOfRangeException(nameof(datacenterId), $"不能大于 {maxDatacenterId} 或小于 0");

            WorkerId = workerId;
            DatacenterId = datacenterId;
            Sequence = 0L;
            LastTimestamp = -1L;
        }
        #endregion

        #region 核心方法
        /// <summary>
        /// 获得下一个ID,线程安全,19位有序数字
        /// </summary>
        /// <returns></returns>
        public long NextId()
        {
            lock (syncRoot)
            {
                long timestamp = GetCurrentTimestamp();
                if (timestamp > LastTimestamp) // 时间戳改变,毫秒内序列重置
                {
                    Sequence = 0L;
                }
                else if (timestamp == LastTimestamp) // 如果是同一时间生成的,则进行毫秒内序列
                {
                    Sequence = (Sequence + 1) & sequenceMask;
                    if (Sequence == 0) // 毫秒内序列溢出
                    {
                        timestamp = GetNextTimestamp(LastTimestamp); // 阻塞到下一个毫秒,获得新的时间戳
                    }
                }
                else // 当前时间小于上一次ID生成的时间戳,证明系统时钟被回拨,此时需要做回拨处理
                {
                    Sequence = (Sequence + 1) & sequenceMask;
                    if (Sequence > 0)
                    {
                        timestamp = LastTimestamp; // 停留在最后一次时间戳上,等待系统时间追上后即完全度过了时钟回拨问题。
                    }
                    else // 毫秒内序列溢出
                    {
                        timestamp = LastTimestamp + 1; // 直接进位到下一个毫秒
                    }
                }

                LastTimestamp = timestamp; // 上次生成ID的时间截

                // 移位并通过或运算拼到一起组成64位的ID
                var id = (timestamp << timestampLeftShift)
                        | (DatacenterId << datacenterIdShift)
                        | (WorkerId << workerIdShift)
                        | Sequence;
                return id;
            }
        }

        /// <summary>尝试分析</summary>
        /// <param name="id"></param>
        /// <param name="time">时间</param>
        /// <param name="workerId">节点</param>
        /// <param name="datacenterId">数据中心</param>
        /// <param name="sequence">序列号</param>
        /// <returns></returns>
        public bool TryParse(long id, out DateTime time, out int workerId, out int datacenterId, out int sequence)
        {
            var timestamp = id >> timestampLeftShift;
            time = StartTimestamp.AddMilliseconds(timestamp);

            var dataId = (id ^ (timestamp << timestampLeftShift)) >> datacenterIdShift;
            var workId = (id ^ ((timestamp << timestampLeftShift) | (dataId << datacenterIdShift))) >> workerIdShift;
            var seq = id & sequenceMask;

            datacenterId = (int)dataId;
            workerId = (int)workId;
            sequence = (int)seq;

            return true;
        }

        /// <summary>
        /// 阻塞到下一个毫秒,直到获得新的时间戳
        /// </summary>
        /// <param name="lastTimestamp">上次生成ID的时间截</param>
        /// <returns>当前时间戳</returns>
        private long GetNextTimestamp(long lastTimestamp)
        {
            long timestamp = GetCurrentTimestamp();
            while (timestamp <= lastTimestamp)
            {
                timestamp = GetCurrentTimestamp();
            }
            return timestamp;
        }

        /// <summary>
        /// 获取当前时间戳
        /// </summary>
        /// <returns></returns>
        private long GetCurrentTimestamp()
        {
            return (long)(DateTime.Now - StartTimestamp).TotalMilliseconds;
        }
        #endregion
    }
}

调用:

//根据数据中心,机房,保证生成id全局唯一
Snowflake snowflake = new Snowflake(workerId: 1, datacenterId: 1);

//主键
long id = snowflake.NextId();
  • 0
    点赞
  • 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
发出的红包

打赏作者

王焜棟琦

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值