生成分布式随机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

原文地址:https://www.cnblogs.com/zhouxiuquan/p/10476944.html

时间: 2024-10-27 13:54:46

生成分布式随机ID的相关文章

分布式唯一id:snowflake算法思考

匠心零度 转载请注明原创出处,谢谢! 缘起 为什么会突然谈到分布式唯一id呢?原因是最近在准备使用RocketMQ,看看官网介绍: 一句话,消息可能会重复,所以消费端需要做幂等.为什么消息会重复后续RocketMQ章节进行详细介绍,本节重点不在这里. 为了达到业务的幂等,必须要有这样一个id存在,需要满足下面几个条件: 同一业务场景要全局唯一. 该id必须是在消息的发送方进行产生发送到MQ. 消费端根据该id进行判断是否重复,确保幂等. 在那里产生,和消费端进行判断等和这个id没有关系,这个id

分布式Unique ID的生成方法

分布式Unique ID的生成方法 分布式的Unique ID的用途如此广泛,从业务对象Id到日志的TraceId,本文总结了林林总总的各种生成算法. 1. 发号器 我接触的最早的Unique ID,就是Oracle的自增ID. 特点是准连续的自增数字,为什么说是准连续?因为性能考虑,每个Client一次会领20个ID回去慢慢用,用完了再来拿.另一个Client过来,拿的就是另外20个ID了. 新浪微博里,Tim用Redis做相同的事情,Incr一下拿一批ID回去.如果有多个数据中心,那就拿高位

分布式全局ID生成方案

传统的单体架构的时候,我们基本是单库然后业务单表的结构.每个业务表的ID一般我们都是从1增,通过AUTO_INCREMENT=1设置自增起始值,但是在分布式服务架构模式下分库分表的设计,使得多个库或多个表存储相同的业务数据.这种情况根据数据库的自增ID就会产生相同ID的情况,不能保证主键的唯一性. 如上图,如果第一个订单存储在 DB1 上则订单 ID 为1,当一个新订单又入库了存储在 DB2 上订单 ID 也为1.我们系统的架构虽然是分布式的,但是在用户层应是无感知的,重复的订单主键显而易见是不

生成随机id对比

生成随机id 最近公司的项目游戏生成的随机不重复id,重复概率有点大, 代码如下: 1 private static int id = 0; 2 public static int serverID = 0; 3 private static final Object obj = new Object(); 4 5 public static long getId1() { 6 synchronized (obj) { 7 id += 1; 8 return (serverID & 0xFFFF

分布式唯一ID的几种生成方案

前言 在互联网的业务系统中,涉及到各种各样的ID,如在支付系统中就会有支付ID.退款ID等.那一般生成ID都有哪些解决方案呢?特别是在复杂的分布式系统业务场景中,我们应该采用哪种适合自己的解决方案是十分重要的.下面我们一一来列举一下,不一定全部适合,这些解决方案仅供你参考,或许对你有用. 正文 分布式ID的特性 唯一性:确保生成的ID是全网唯一的. 有序递增性:确保生成的ID是对于某个用户或者业务是按一定的数字有序递增的. 高可用性:确保任何时候都能正确的生成ID. 带时间:ID里面包含时间,一

Java秒杀系统实战系列~分布式唯一ID生成订单编号

摘要: 本篇博文是"Java秒杀系统实战系列文章"的第七篇,在本博文中我们将重点介绍 "在高并发,如秒杀的业务场景下如何生成全局唯一.趋势递增的订单编号",我们将介绍两种方法,一种是传统的采用随机数生成的方式,另外一种是采用当前比较流行的"分布式唯一ID生成算法-雪花算法"来实现. 内容: 在上一篇博文,我们完成了商品秒杀业务逻辑的代码实战,在该代码中,我们还实现了"当用户秒杀成功后,需要在数据库表中为其生成一笔秒杀成功的订单记录&qu

一文读懂分布式唯一ID生成

很多大的互联网公司数据量很大,都采用分库分表,那么分库后就需要统一的唯一ID进行存储.这个ID可以是数字递增的,也可以是UUID类型的. 如果是递增的话,那么拆分了数据库后,可以按照id的hash,均匀的分配到数据库中,并且mysql数据库如果将递增的字段作为主键存储的话会大大提高存储速度.但是如果把订单ID按照数字递增的话,别人能够很容易猜到你有多少订单了,这种情况就可以需要一种非数字递增的方式进行ID的生成. 想到分布式ID的生成,大家可能想到采用Redis进行生成ID,使用Redis的IN

分布式唯一ID生成算法-雪花算法

在我们的工作中,数据库某些表的字段会用到唯一的,趋势递增的订单编号,我们将介绍两种方法,一种是传统的采用随机数生成的方式,另外一种是采用当前比较流行的“分布式唯一ID生成算法-雪花算法”来实现. 一.时间戳随机数生成唯一ID 我们写一个for循环,用RandomUtil.generateOrderCode()生成1000个唯一ID,执行结果我们会发现出现重复的ID. /** * 随机数生成util **/ public class RandomUtil { private static fina

mysql生成随机id

MySQL中生成随机ID的函数是UUID(),但是这样生成出来的随机ID是36位带[-]符号的. SELECT UUID(); -- 37747019-90a2-11e9-9806-00505683703f 我们可以配合REPLACE()函数替换掉[-]符号来生成32位的不带[-]符号的随机ID. SELECT REPLACE(UUID(), '-', ''); -- 62d1556390a211e9980600505683703f 我们也可以配合UPPER()函数将小写字母转换为大写. SEL