【高性能】生成惟一時間戳ID,1毫秒預計能生成1000個

凡事涉及到高性能貌似都是高大上的東西,因此嘛我也試試;其實這個時間戳ID的生成主要爲了解決咱們公司內部的券號生成,估計有小夥伴認爲券號生成有這麼麻煩嘛,搞個自增ID徹底能夠用起來,或者時間取毫微米時間戳等。算法

若是以上真是這樣簡單的話,那我要說道說道;首先自增ID資源耗盡的時候,特別禮券號生成的越頻繁,畢竟bigInt也有耗盡那天(固然若是有更長數字字段就是慢慢耗唄),並且依靠數據庫進行被動生成,在有些業務上比較軟肋;我還有一個同事說搞一張表定時去自增生成ID,這樣就能隨時取已經存在的ID資源數據,我只能說這是一種笨辦法,並且你都不知道外部業務對券號的須要量有多少,萬一立馬要個1000萬,你來得及?數據庫

還有就是毫微米時間戳也是會出問題,由於在多併發請求下也會大機率出現一樣ID,你們不信能夠去試試,想一想咱們的CPU運算有多快;固然防止重複能夠考慮休眠例如一毫秒,但這樣就會丟失性能,想一想雪花算法一毫秒能產生4095個不重複ID,咱們好歹也能夠向它學習吧,以上說了這麼多少就明白了這個要求也是蠻苛刻的,當中我也想過用雪花算法,但因爲生成的ID比較長(後面我會說爲何不適宜)!服務器

下面來看看我對這個惟一時間戳ID生成的代碼算法,借鑑了點雪花算法:併發

   /// <summary>
    /// 時間戳ID
    /// </summary>
    public class TimestampID
    {
        private long _lastTimestamp;
        private long _sequence; //計數從零開始
        private readonly DateTime? _initialDateTime;
        private static TimestampID _timestampID;
        private const int MAX_END_NUMBER = 9999;

        private TimestampID(DateTime? initialDateTime)
        {
            _initialDateTime = initialDateTime;
        }

        /// <summary>
        /// 獲取單個實例對象
        /// </summary>
        /// <param name="initialDateTime">最初時間,與當前時間作個相差取時間戳</param>
        /// <returns></returns>
        public static TimestampID GetInstance(DateTime? initialDateTime = null)
        {
            if (_timestampID == null) Interlocked.CompareExchange(ref _timestampID, new TimestampID(initialDateTime), null);
            return _timestampID;
        }

        /// <summary>
        /// 最初時間,做用時間戳的相差
        /// </summary>
        protected DateTime InitialDateTime
        {
            get
            {
                if (_initialDateTime == null || _initialDateTime.Value == DateTime.MinValue) return new DateTime(1970, 1, 1, 0, 0, 0, DateTimeKind.Utc);
                return _initialDateTime.Value;
            }
        }
        /// <summary>
        /// 獲取時間戳ID
        /// </summary>
        /// <returns></returns>
        public string GetID()
        {
            long temp;
            var timestamp = GetUniqueTimeStamp(_lastTimestamp, out temp);
            return $"{timestamp}{Fill(temp)}";
        }

        private string Fill(long temp)
        {
            var num = temp.ToString();
            IList<char> chars = new List<char>();
            for (int i = 0; i < MAX_END_NUMBER.ToString().Length - num.Length; i++)
            {
                chars.Add('0');
            }
            return new string(chars.ToArray()) + num;
        }

        /// <summary>
        /// 獲取一個時間戳字符串
        /// </summary>
        /// <returns></returns>
        public long GetUniqueTimeStamp(long lastTimeStamp, out long temp)
        {
            lock (this)
            {
                temp = 1;
                var timeStamp = GetTimestamp();
                if (timeStamp == _lastTimestamp)
                {
                    _sequence = _sequence + 1;
                    temp = _sequence;
                    if (temp >= MAX_END_NUMBER)
                    {
                        timeStamp = GetTimestamp();
                        _lastTimestamp = timeStamp;
                        temp = _sequence = 1;
                    }
                }
                else
                {
                    _sequence = 1;
                    _lastTimestamp = timeStamp;
                }
                return timeStamp;
            }
        }

        /// <summary>
        /// 
        /// </summary>
        /// <returns></returns>
        private long GetTimestamp()
        {
            if (InitialDateTime >= DateTime.Now) throw new Exception("最初時間比當前時間還大,不合理");
            var ts = DateTime.UtcNow - InitialDateTime;
            return (long)ts.TotalMilliseconds;
        }
    }

當中我加了一點補位算法,保證每次出來的ID長度一致,以前提到了是用在禮券號上的,那就應該不能這麼長,後續我又繼續進行了32進制計算,縮短到8-10位左右,但你們估計覺的仍是長,那就看取決你把相差時間應該縮短。但若是直接用雪花算法生成的ID進行32位進制縮短也是在10位以上,因此我沒有用到。負載均衡

對了,忘記說了性能問題,一毫秒預計能生成1000個,呵呵,還算過得去分佈式

接下來談談禮券這塊業務,相似咱們初創電商公司這種須要去互聯網上大量拉攏會員,因此也相對須要大量的推廣禮券號,若是成熟的電商如京東和天貓等,他們全部禮券都已經綁定到本身會員身上,在使用上根本不用去關注填寫什麼禮券號,也是他們的禮券體系相對完整和成熟,故咱們對禮券號的的生成需求也是一塊心病。性能

下面再說說雪花算法生成的ID,比較適合使用一些流水數據,若是分佈式上生成時就須要考慮一臺吞吐量好的服務統一輩子成ID,或者也能夠進行多臺服務器+負載均衡,固然每臺機器出的ID仍是須要標識補位(好比機器自定義的編號ID)增長長度防止同一時間重複ID。學習

以上若有不對之處請留言,你們共同窗習進步!!!this

 

2017.1.22 > 在生產環境中,忽然又發生了禮券號重複問題,致使我懷疑算法的不嚴謹,後來發現觸發生成的來源是兩個服務進程,哈哈,真是本身找坑跳;故你們在部署時候必定要確保ID生成在一個進程裏,若是分佈式仍是老話,加上必要的分佈標識NOspa

相關文章
相關標籤/搜索