生成肉眼【可视】的有序分布式ID

去年做了一个产品,会经常导入导出大量的外部数据,这些数据的ID有的是GUID类型,有的是字符串,也有的是自增。GUID类型没有顺序,结果要排序得借助其它业务字段,整体查询效率比较低;字符串ID本来是用来转换GUID的或者数字ID的,结果有些字符串ID不符合规范,常常有特殊数据需要处理;自增主键ID的数据导入无法合并经常有冲突。

为了避免GUID主键的“索引页分裂”问题,提高查询效率,同时为了解决分布式环境下的数据导入合并问题,强烈需要一种分布式的,有序的ID生成方案。我参考了雪花ID(Twitter-Snowflake),64位自增ID算法实现方案,设计一个更容易肉眼观察数值连续有序的分布式ID方案。

跟雪花ID方案一样,都是使用时间数据做为生成ID的基础,不同的在于对数据的具体处理方式。另外,为了确保每台机器ID的不同,我采取了根据机器IP随机生成3位分布式机器ID的方法。该算法的实现比雪花算法简单不少,详细的不多说,直接看代码:

   /// <summary>
        /// 获取一个新的有序GUID整数
        /// </summary>
        /// <param name="dt">当前时间</param>
        /// <param name="haveMs">是否包含毫秒,生成更加有序的数字,但这会增加重复率</param>
        /// <returns></returns>
        protected internal static long InnerNewSequenceGUID(DateTime dt, bool haveMs)
        {
            //日期以 2017.3.1日为基准,计算当前日期距离基准日期相差的天数,可以使用20年。
            //日期部分使用4位数字表示
            int days = (int)dt.Subtract(baseDate).TotalDays;
            //时间部分表示一天中所有的秒数,最大为 86400秒,共5位
            //日期时间总位数= 4(日期)+5(时间)+3(毫秒)=12
            int times = dt.Second + dt.Minute * 60 + dt.Hour * 3600;
            //long 类型最大值 9223 3720 3685 4775 807
            //可用随机位数= 19-12=7
            long datePart = ((long)days + 1000) * 1000 * 1000 * 1000 * 100;
            long timePart = (long)times * 1000 * 1000;
            long msPart = (long)dt.Millisecond * 1000;
            long dateTiePart = (datePart + timePart + msPart) * 10000;

            int mid = MachineID * 10000;
            //得到总数= 4(日期)+5(时间)+3(毫秒)+7(GUID)
            long seq = dateTiePart + mid;

            //线程安全的自增并且不超过最大值10000
            int startValue = System.Threading.Interlocked.Increment(ref SeqNum);
            while (startValue >= 10000)
            {
                SeqNum = 0;
                startValue = 0;
                //可能此时别的线程再次更改了 SeqNum
                while (startValue != SeqNum)
                {
                    startValue = System.Threading.Interlocked.Increment(ref SeqNum);
                }
            }

            seq = seq + startValue;
            return seq;
        }

该方法进行了再次封装,用于在不同情况下分别使用:

   /// <summary>
       /// 生成一个新的有序的长整形“GUID”,在一秒内,重复概率低于 万分之一,速度较快,线程安全,
       /// 但不如NewUniqueSequenceGUID 方法结果更有序(不包含毫秒部分)
       /// </summary>
       /// <returns></returns>
       public static long NewSequenceGUID()
       {
           return UniqueSequenceGUID.InnerNewSequenceGUID(DateTime.Now,false);
       }

       /// <summary>
       /// 生成一个唯一的有序的GUID形式的长整数,速度较NewSequenceGUID 稍慢,但线程不安全
       /// </summary>
       /// <returns></returns>
       public static long NewUniqueSequenceGUID()
       {
           return UniqueId.NewID();
       }

        /// <summary>
        /// 当前机器ID,可以作为分布式ID,如果需要指定此ID,请在应用程序配置文件配置 SOD_MachineID 的值,范围大于100,小于1000.
        /// </summary>
        /// <returns></returns>
        public static int CurrentMachineID()
        {
            return UniqueSequenceGUID.GetCurrentMachineID();
        }

最后,像下面这样使用即可:

        Console.WriteLine("当前机器的分布式ID:{0}",CommonUtil.CurrentMachineID());
            Console.WriteLine("测试分布式ID:快速(万分之一重复率)模式");
            for (int i= 0; i < 50; i++)
            {
                Console.Write(CommonUtil.NewSequenceGUID());
                Console.Write(",");
            }
            Console.WriteLine();
            Console.WriteLine("测试分布式ID:唯一模式");
            for (int i = 0; i < 50; i++)
            {
                Console.Write(CommonUtil.NewUniqueSequenceGUID());
                Console.Write(",");
            }
            Console.WriteLine();

下面是生成的ID数字示例:

当前机器的分布式ID:832
测试分布式ID:快速(万分之一重复率)模式
1455630735488320001,1455630735498320002,1455630735498320003,1455630735498320004,1455630735498320005,1455630735498320006,1455630735498320007,1455630735498320008,1455630735498320009,1455630735498320010,1455630735508320011,1455630735508320012,1455630735508320013,1455630735508320014,1455630735508320015,1455630735508320016,1455630735508320017,1455630735508320018,1455630735508320019,1455630735508320020,1455630735508320021,1455630735508320022,1455630735508320023,1455630735508320024,1455630735508320025,1455630735508320026,1455630735508320027,1455630735508320028,1455630735508320029,1455630735508320030,1455630735508320031,1455630735508320032,1455630735508320033,1455630735518320034,1455630735518320035,1455630735518320036,1455630735518320037,1455630735518320038,1455630735518320039,1455630735518320040,1455630735528320041,1455630735528320042,1455630735528320043,1455630735528320044,1455630735528320045,1455630735528320046,1455630735528320047,1455630735528320048,1455630735528320049,1455630735558320050,
测试分布式ID:唯一模式
1455630735578320051,1455630735598320052,1455630735598320053,1455630735598320054,1455630735598320055,1455630735608320056,1455630735608320057,1455630735608320058,1455630735608320059,1455630735608320060,1455630735608320061,1455630735608320062,1455630735608320063,1455630735618320064,1455630735618320065,1455630735618320066,1455630735618320067,1455630735618320068,1455630735618320069,1455630735618320070,1455630735618320071,1455630735628320072,1455630735628320073,1455630735628320074,1455630735628320075,1455630735628320076,1455630735628320077,1455630735628320078,

注:本文生成ID的方法已经在产品中大量使用,运行情况良好。

要使用本程序,你可以Nuget 下载SOD的程序包,然后像本文示例这样使用即可:

Install-Package PDF.NET.SOD.Core

获取SOD的源码,请Fork我们的Github:

https://github.com/znlgis/sod

源码位置在 https://github.com/znlgis/sod/tree/master/src/SOD 目录下。

有疑问,请加QQ群154224970 咨询。

猜你喜欢

转载自www.cnblogs.com/bluedoctor/p/9112269.html