zoukankan      html  css  js  c++  java
  • TwitterSnowflake,64位自增ID算法详解

    Twitter-Snowflake算法产生的背景相当简单,为了满足Twitter每秒上万条消息的请求,每条消息都必须分配一条唯一的id,这些id还需要一些大致的顺序(方便客户端排序),并且在分布式系统中不同机器产生的id必须不同。

    snowflake把时间戳工作机器id序列号组合在一起。

    snowflake-64bit

    除了最高位bit标记为不可用以外,其余三组bit占位均可浮动,看具体的业务需求而定。以下关于此算法的可行性研究

     Console.WriteLine("41bit的时间戳可以支持该算法使用年限:{0}", (1L << 41) / (3600L * 24 * 365 * 1000.0));
     Console.WriteLine("10bit的工作机器id数量:{0}", (1L << 10) - 1);
     Console.WriteLine("12bit序列id数量:{0}", (1L << 12) - 1);

    运行结果:

    41bit的时间戳可以支持该算法使用年限:69.7305700010147
    10bit的工作机器id数量:1023
    12bit序列id数量:4095

    默认情况下41bit的时间戳(从当前开始计算)可以支持该算法使用近70年,10bit的工作机器id可以支持1023台机器,序列号支持1毫秒产生4095个自增序列id。那么理论上,一个应用1秒钟可以产生409万条自增ID,此算法可持续使用近70年。完全能满足我们日常开发项目的需求。

    工作机器id严格意义上来说这个bit段的使用可以是进程级,机器级的话你可以使用MAC地址来唯一标示工作机器,工作进程级可以使用IP+Path来区分工作进程。如果工作机器比较少,可以使用配置文件来设置这个id是一个不错的选择,如果机器过多配置文件的维护是一个灾难性的事情。这个工作机器id的bit段也可以进一步拆分,比如用前5个bit标记workerid,后5个bit标记datacenterid,具体代码如下:

        class Snowflake
        {
    
            //工作机器id的bit段拆分为前5个bit标记workerid,后5个bit标记datacenterid
            const int WorkerIdBits = 5;
            const int DatacenterIdBits = 5;
            //序列号bit数
            const int SequenceBits = 12;
            //最大编号限制
            const long MaxWorkerId = -1L ^ (-1L << WorkerIdBits);
            const long MaxDatacenterId = -1L ^ (-1L << DatacenterIdBits);
            private const long SequenceMask = -1L ^ (-1L << SequenceBits);
            //位左运算移动量
            public const int WorkerIdShift = SequenceBits;
            public const int DatacenterIdShift = SequenceBits + WorkerIdBits;
            public const int TimestampLeftShift = SequenceBits + WorkerIdBits + DatacenterIdBits;
    
            //序列号记录
            private long _sequence = 0L;
            //时间戳记录
            private long _lastTimestamp = -1L;
    
    
            public long WorkerId { get; protected set; }
            public long DatacenterId { get; protected set; }
    
            public Snowflake(long workerId, long datacenterId, long sequence = 0L) 
            {
                WorkerId = workerId;
                DatacenterId = datacenterId;
                _sequence = sequence;
            
                // sanity check for workerId
                if (workerId > MaxWorkerId || workerId < 0) 
                {
                    throw new ArgumentException( String.Format("worker Id can't be greater than {0} or less than 0", MaxWorkerId) );
                }
    
                if (datacenterId > MaxDatacenterId || datacenterId < 0)
                {
                    throw new ArgumentException( String.Format("datacenter Id can't be greater than {0} or less than 0", MaxDatacenterId));
                }
    
            }
            /// <summary>
            /// 格林时间戳
            /// </summary>
            /// <returns></returns>
            public long TimeGen()
            {
                DateTime Jan1st1970 = new DateTime(1970, 1, 1, 0, 0, 0, DateTimeKind.Utc);//
                return (long)(DateTime.UtcNow - Jan1st1970).TotalMilliseconds;
            }
    
            readonly object _lock = new Object();
    
            public virtual long NextId()
            {
                lock (_lock)
                {
                    var timestamp = TimeGen();
    
                    if (timestamp < _lastTimestamp)
                    {
                        throw new InvalidSystemClock(String.Format(
                            "发现最新时间戳少{0}毫秒的异常", _lastTimestamp - timestamp));
                    }
    
                    if (_lastTimestamp == timestamp)
                    {
                        _sequence = (_sequence + 1) & SequenceMask;
                        if (_sequence == 0)
                        {
                            //序列号超过限制,重新取时间戳
                            timestamp = TilNextMillis(_lastTimestamp);
                        }
                    }
                    else
                    {
                        _sequence = 0;
                    }
    
                    _lastTimestamp = timestamp;
                    //snowflake算法
                    var id = (timestamp << TimestampLeftShift) |
                             (DatacenterId << DatacenterIdShift) |
                             (WorkerId << WorkerIdShift) | _sequence;
    
                    return id;
                }
            }
            /// <summary>
            /// 重新取时间戳
            /// </summary>
            protected virtual long TilNextMillis(long lastTimestamp)
            {
                var timestamp = TimeGen();
                while (timestamp <= lastTimestamp)
                {
                    //新的时间戳要大于旧的时间戳,才算作有效时间戳
                    timestamp = TimeGen();
                }
                return timestamp;
            }
        }
  • 相关阅读:
    ASP.NET 学习日志
    igoogle 小工具
    nios ann 语音识别
    ASP 3.5 读书笔记
    C# delegate and event 续
    paper
    网站白痴的 ASP.NET website 学习日志
    盒子模型
    将对象序列化成json
    不错的Oracle 存储过程例子
  • 原文地址:https://www.cnblogs.com/songshuai/p/5603111.html
Copyright © 2011-2022 走看看