zoukankan      html  css  js  c++  java
  • 分步式系统中全局唯一主键

    /// <summary>
        /// 分步式全局唯一Id生成算法
        /// 根据SnowFlake算法生成全局唯一Id.(由时间戳,区域位,站点位,随机数四部分组成)
        /// </summary>
        public static class GlobalUniqueId
        {
            /// <summary>
            ////// </summary>
            private static readonly Object LockObj = new Object();
    
            /// <summary>
            /// 纪元开始
            /// </summary>
            private static readonly DateTime BaseDate = new DateTime(2016, 1, 1, 0, 0, 0);
    
            /// <summary>
            /// 22~62位(从低位到高位 0~63位)共41个位用来记录时间戳(毫秒)
            /// </summary>
            private static readonly int MillscondsBits = 41;
    
            /// <summary>
            /// 17~21位(从低位到高位 0~63位)共5个位用来记录区域id。
            /// </summary>
            private static readonly int DatacenterIdBits = 5;
    
            /// <summary>
            /// 12~16位(从低位到高位 0~63位)共5个位用来记录工作机器id。
            /// </summary>
            private static readonly int WorkerIdBits = 5;
    
            /// <summary>
            /// 0~11位(从低位到高位 0~63位)共12个位用来记录序列号(同毫秒内产生的不同id)。
            /// </summary>
            private static readonly int SequenceBits = 12;
    
            /// <summary>
            /// 最大区域Id
            /// </summary>
            private static readonly int MaxDataCenterId = (1 << DatacenterIdBits) - 1;
            /// <summary>
            /// 最大站点Id
            /// </summary>
            private static readonly int MaxWorkerId = (1 << WorkerIdBits) - 1;
    
            /// <summary>
            /// 时间戳左移位
            /// </summary>
            private static readonly int TimestampLeftShift = SequenceBits + WorkerIdBits + DatacenterIdBits;
    
            /// <summary>
            /// 区域左移位
            /// </summary>
            private static readonly int DatacenterIdLeftShift = SequenceBits + WorkerIdBits;
    
            /// <summary>
            /// 站点左移位
            /// </summary>
            private static readonly int WorkerIdLeftShift = SequenceBits;
    
            /// <summary>
            /// 最大序列号 
            /// </summary>
            private static readonly int SequenceMask = (1 << SequenceBits) - 1; //4095
    
            /// <summary>
            /// 当前序列号
            /// </summary>
            private static int _sequence;
    
            /// <summary>
            /// 上次时间戳
            /// </summary>
            private static long _lastTimestamp = -1L;
    
            /// <summary>
            /// 获取时间戳
            /// </summary>
            /// <returns></returns>
            private static long GetMillSeconds()
            {
                var ts = DateTime.Now - BaseDate;
                return (long)ts.TotalMilliseconds;
            }
    
            private static long NextMillis(long lastTimestamp)
            {
                long timestamp = GetMillSeconds();
                while (timestamp <= lastTimestamp)
                {
                    timestamp = GetMillSeconds();
                }
                return timestamp;
            }
    
            /// <summary>
            /// 计算 当前时间毫秒位 与 当前时间的序列位
            /// </summary>
            /// <returns></returns>
            private static long GenerateTimestampAndSequence(ref int sequence)
            {
                long timestamp = GetMillSeconds();
    
                if (timestamp < _lastTimestamp)
                {
                    throw new Exception(
                        String.Format("Clock moved backwards.  Refusing to generate id for {0} milliseconds",
                            _lastTimestamp - timestamp));
                }
    
                if (_lastTimestamp == timestamp)
                {
                    sequence = (sequence + 1) & SequenceMask;
                    if (sequence == 0)
                    {
                        timestamp = NextMillis(_lastTimestamp);
                    }
                }
                else
                {
                    sequence = 0;
                }
    
                _lastTimestamp = timestamp;
                _sequence = sequence;
    
                return timestamp;
            }
    
            /// <summary>
            /// 根据SnowFlake算法生成全局唯一Id.(由时间戳,区域位,站点位,序列数四部分组成)
            /// </summary>
            /// <param name="dataCenterId"></param>
            /// <param name="workerId"></param>
            /// <returns></returns>
            public static long NextId(byte dataCenterId, ushort workerId)
            {
                if (dataCenterId >= MaxDataCenterId)
                {
                    throw new Exception("区域号超过最大值");
                }
    
                if (workerId >= MaxWorkerId)
                {
                    throw new Exception("机器号超过最大值");
                }
    
                int sequence = 0;
                long ms = 0;
                lock (LockObj)
                {
                    //计算 当前时间毫秒位 与 当前时间的序列位
                    sequence = _sequence;
                    ms = GenerateTimestampAndSequence(ref sequence);
                }
    
                return (ms << TimestampLeftShift) //生成 当前时间毫秒位 
                       | (dataCenterId << DatacenterIdLeftShift) //生成区域位
                       | (workerId << WorkerIdLeftShift) //生成站点位
                       | sequence; //生成序列位
            }
    
            /// <summary>
            /// 默认分布式Id生成算法生成的全局唯一值
            /// </summary>
            /// <returns></returns>
            public static long NextId()
            {
                return NextId(1, 1);
            }
        }

    前几天看到一篇Java版的全局唯一主键,觉得写的很不错,就直接简化了成C#,

    long共 64个字节

    其中0-9是随机数,10-19是计算机唯一值,20-22是区域唯一值,23-63是时间毫秒数

    这样 随机数小于 2^10=1024

    计算机唯一值 小于 2^10=1024

    区域唯一值 小于 2^3=8

    毫秒数 2^41,可以用上 99年(感觉有点少啊)

    class GlobalUniqueId
    {
    const int MaxRegionId = 8;
    const int MaxComputerId = 1024;
    private long _regionId;
    private long _computerId;
    static GlobalUniqueId _Instance = null;
    private GlobalUniqueId()
    {
    }
    static object objLock = new object();
    public static GlobalUniqueId Instance
    {
    get
    {
    if (_Instance == null)
    {
    lock (objLock)
    {
    if (_Instance == null)
    _Instance = new GlobalUniqueId();
    }
    }
    return _Instance;
    }
    }
    static readonly DateTime BASE_DATE = new DateTime(2010, 1, 1, 0, 0, 0);

    long getMillSeconds()
    {
    TimeSpan ts = DateTime.Now - BASE_DATE;
    return (long)ts.TotalMilliseconds;
    }
    void addRegionId(ref long r)
    {

    r = r | (_regionId << (64 - 41 - 3));

    }
    void addComputerId(ref long r)
    {

    r = r | (_computerId << (64 - 41 - 3 - 10));

    }
    void addMillSecondId(ref long r)
    {
    long ms = getMillSeconds();
    r = r | (ms << (64 - 41));

    }
    static readonly Random rand = new Random();
    static readonly int MaxRandId = (int)(Math.Pow(2, 10));
    void addRandomId(ref long r)
    {
    long rndId = rand.Next(0, MaxRandId);

    r = r | rndId;

    }
    public long NextId(int regionId, int computerId)
    {
    if (regionId >= MaxRegionId) throw new Exception("区域号超过最大值");
    if (computerId >= MaxComputerId) throw new Exception("机器号超过最大值");
    this._regionId = regionId;
    this._computerId = computerId;
    long r = 0;//
    addMillSecondId(ref r);
    addRegionId(ref r);
    addComputerId(ref r);
    addRandomId(ref r);
    return r;
    }
    }

  • 相关阅读:
    通过实例来分析I2C基本通信协议
    POJ 1470 Closest Common Ancestors【近期公共祖先LCA】
    并发 错误 java.lang.IllegalMonitorStateException: current thread not owner 分析
    【Nginx】HTTP请求的11个处理阶段
    JavaScript获取当前值
    air游戏接入小米支付sdk
    [学习笔记]Pollard-Rho
    2018-2-13-win10-UWP-Markdown-含源代码
    2018-2-13-win10-UWP-Markdown-含源代码
    2018-2-13-win10-uwp-图标制作器
  • 原文地址:https://www.cnblogs.com/zhshlimi/p/5370215.html
Copyright © 2011-2022 走看看