生成分布式随机ID

经测试,最快的一种

public class Generator
    {
        // should be between 40 (34 years) and 42 (139 years)
        internal const int NumberOfTimeBits = 42;

        // should be between 0 (single generator) and 10 (1024 generators)
        internal const int NumberOfGeneratorIdBits = 9;

        // should be 10 at least (4096 unique ids per millisecond per generator)
        internal const int NumberOfSequenceBits = 64 - NumberOfTimeBits - NumberOfGeneratorIdBits;

        private readonly byte[] _buffer = new byte[8];
        private readonly byte[] _timeBytes = new byte[8];
        private readonly byte[] _idBytes = new byte[2];
        private readonly byte[] _sequenceBytes = new byte[2];
        private readonly int _maxSequence = (int)Math.Pow(2, NumberOfSequenceBits) - 1;
        private readonly DateTime _start;

        private short _sequence;
        private long _previousTime;

        /// <summary>
        /// Instantiate the generator. Each Generator should have its own ID, so you can
        /// use multiple Generator instances in a cluster. All generated IDs are unique
        /// provided the start date newer changes. I recommend to choose January 1, 2013.
        /// </summary>
        public Generator(short generatorId, DateTime start)
        {
            if (generatorId < 0 || generatorId >= Math.Pow(2, NumberOfGeneratorIdBits))
            {
                var msg = string.Format(
                    CultureInfo.InvariantCulture,
                    "generator id must be between 0 (inclusive) and {0} (exclusive).",
                    Math.Pow(2, NumberOfGeneratorIdBits));
                throw new ArgumentException(msg, "generatorId");
            }
            if (start > DateTime.Today)
            {
                throw new ArgumentException("start date must not be in the future.", "start");
            }

            CalculateIdBytes(generatorId);
            _start = start;
        }

        /// <summary>
        /// Can generate up to 4096 different IDs per millisecond.
        /// </summary>
        public string Next()
        {
            SpinToNextSequence();
            WriteValuesToByteArray(_buffer, _previousTime, _sequence);

            //return DateTime.Now.ToString("yyyyMMddHH")+Convert.ToBase64String(_buffer); 这样有更好的排序
            return Convert.ToBase64String(_buffer);
        }

        public ulong NextLong()
        {
            SpinToNextSequence();
            WriteValuesToByteArray(_buffer, _previousTime, _sequence);

            Array.Reverse(_buffer);
            return BitConverter.ToUInt64(_buffer, 0);
        }

        internal unsafe void WriteValuesToByteArray(byte[] target, long time, short sequence)
        {
            fixed (byte* arrayPointer = target)
            {
                *(long*)arrayPointer = 0;
            }

            fixed (byte* arrayPointer = _timeBytes)
            {
                *(long*)arrayPointer = time << (64 - NumberOfTimeBits);
            }

            fixed (byte* arrayPointer = _sequenceBytes)
            {
                *(short*)arrayPointer = sequence;
            }

            WriteValuesToByteArray(target, _timeBytes, _idBytes, _sequenceBytes);
        }

        private unsafe void CalculateIdBytes(short id)
        {
            fixed (byte* arrayPointer = _idBytes)
            {
                *(short*)arrayPointer = (short)(id << (8 - ((64 - NumberOfSequenceBits) % 8)));
            }
        }

        [MethodImpl(MethodImplOptions.AggressiveInlining)]
        private void WriteValuesToByteArray(byte[] target, byte[] time, byte[] id, byte[] sequence)
        {
            ////                                                 1234567890123456789012
            //// time: 1111111111111111111111111111111111111111110000
            //// id:                                           0011111111110000
            //// seq:                                                  0000111111111111
            ////
            ////       000000000000000100010111101010100001000010 0000001011 000000000000
            //// pos:  0         1         2         3         4         5         6
            //// byte: 0       1       2       3       4       5       6       7

            target[0] = (byte)(target[0] | time[7]);
            target[1] = (byte)(target[1] | time[6]);
            target[2] = (byte)(target[2] | time[5]);
            target[3] = (byte)(target[3] | time[4]);
            target[4] = (byte)(target[4] | time[3]);
            target[5] = (byte)(target[5] | time[2]);
            target[6] = (byte)(target[6] | time[1]);
            target[7] = (byte)(target[7] | time[0]);

            target[5] = (byte)(target[5] | id[1]);
            target[6] = (byte)(target[6] | id[0]);

            target[6] = (byte)(target[6] | sequence[1]);
            target[7] = (byte)(target[7] | sequence[0]);
        }

        private void SpinToNextSequence()
        {
            var time = GetTime();

            while (time == _previousTime && _sequence >= _maxSequence)
            {
                time = GetTime();
            }

            _sequence = time == _previousTime ? (short)(_sequence + 1) : (short)0;
            _previousTime = time;
        }

        [MethodImpl(MethodImplOptions.AggressiveInlining)]
        private long GetTime()
        {
            return (long)(DateTime.UtcNow - _start).TotalMilliseconds;
        }
    }

gihub地址:https://github.com/mschuler/UniqueIdGenerator

posted @ 2019-03-05 15:09  百年俊少  阅读(461)  评论(0编辑  收藏  举报