雪花算法为什么以雪花命名?

雪花(snowflake)在自然界中,是极具独特魅力,又变幻莫测的东西:

1、雪花属于六方晶系,它具有四个结晶轴,其中三个辅轴在一个基面上,互相以60度的角度相交,第四轴(主晶轴)与三个辅轴所形成的基面垂直

2、雪花的基本形状是六角形,但是大自然中却几乎找不到俩朵完全相同的雪花,每一个雪花都拥有自己独特的图案,就像地球上找不出两片完全相同的树叶的一样。许多学者用显微镜观测过成千上万朵雪花,这些研究最后表明,形状,大小完全一样和各部分完全对称的雪花在自然界中是无法形成的。

雪花算法_数据结构与算法

雪花算法:

雪花算法_数据结构与算法_02

雪花算法的简单描述:

  • 最高位是符号位,始终为0,不可用。
  • 41位的时间序列,精确到毫秒级,41位的长度可以使用69年。时间位还有一个很重要的作用是可以根据时间进行排序。
  • 10位的机器标识,10位的长度最多支持部署1024个节点。
  • 12位的计数序列号,序列号即一系列的自增id,可以支持同一节点同一毫秒生成多个ID序号,12位的计数序列号支持每个节点每毫秒产生4096个ID序号。

这个算法很简洁实现起来也很简单,是一个很好的ID生成策略。其中,10位机器标识一般是5位IDC+5位machine编号,唯一确定一台机器。

具体操作:

1、先在VS2017中建立一个控制台应用程序,添加一个snowflake类,添加算法如下:

using System;
using System.Collections.Generic;
using System.Linq;
using System.Web;

namespace using和layout
{
    /// <summary>
    /// 分布式ID算法(雪花算法)
    /// </summary>
    public class Snowflake
    {
        private static long machineId;//机器ID
        private static long datacenterId = 0L;//数据ID
        private static long sequence = 0L;//计数从零开始

        private static long twepoch = 687888001020L; //唯一时间随机量

        private static long machineIdBits = 5L; //机器码字节数
        private static long datacenterIdBits = 5L;//数据字节数
        public static long maxMachineId = -1L ^ -1L << (int)machineIdBits; //最大机器ID
        private static long maxDatacenterId = -1L ^ (-1L << (int)datacenterIdBits);//最大数据ID

        private static long sequenceBits = 12L; //计数器字节数,12个字节用来保存计数码        
        private static long machineIdShift = sequenceBits; //机器码数据左移位数,就是后面计数器占用的位数
        private static long datacenterIdShift = sequenceBits + machineIdBits;
        private static long timestampLeftShift = sequenceBits + machineIdBits + datacenterIdBits; //时间戳左移动位数就是机器码+计数器总字节数+数据字节数
        public static long sequenceMask = -1L ^ -1L << (int)sequenceBits; //一微秒内可以产生计数,如果达到该值则等到下一微妙在进行生成
        private static long lastTimestamp = -1L;//最后时间戳

        private static object syncRoot = new object();//加锁对象
        static Snowflake snowflake;

        public static Snowflake Instance()
        {
            if (snowflake == null)
                snowflake = new Snowflake();
            return snowflake;
        }

        public Snowflake()
        {
            Snowflakes(0L, -1);
        }

        public Snowflake(long machineId)
        {
            Snowflakes(machineId, -1);
        }

        public Snowflake(long machineId, long datacenterId)
        {
            Snowflakes(machineId, datacenterId);
        }

        private void Snowflakes(long machineId, long datacenterId)
        {
            if (machineId >= 0)
            {
                if (machineId > maxMachineId)
                {
                    throw new Exception("机器码ID非法");
                }
                Snowflake.machineId = machineId;
            }
            if (datacenterId >= 0)
            {
                if (datacenterId > maxDatacenterId)
                {
                    throw new Exception("数据中心ID非法");
                }
                Snowflake.datacenterId = datacenterId;
            }
        }

        /// <summary>
        /// 生成当前时间戳
        /// </summary>
        /// <returns>毫秒</returns>
        private static long GetTimestamp()
        {
            return (long)(DateTime.UtcNow - new DateTime(1970, 1, 1, 0, 0, 0, DateTimeKind.Utc)).TotalMilliseconds;
        }

        /// <summary>
        /// 获取下一微秒时间戳
        /// </summary>
        /// <param name="lastTimestamp"></param>
        /// <returns></returns>
        private static long GetNextTimestamp(long lastTimestamp)
        {
            long timestamp = GetTimestamp();
            if (timestamp <= lastTimestamp)
            {
                timestamp = GetTimestamp();
            }
            return timestamp;
        }

        /// <summary>
        /// 获取长整型的ID
        /// </summary>
        /// <returns></returns>
        public long GetId()
        {
            lock (syncRoot)
            {
                long timestamp = GetTimestamp();
                if (Snowflake.lastTimestamp == timestamp)
                { //同一微妙中生成ID
                    sequence = (sequence + 1) & sequenceMask; //用&运算计算该微秒内产生的计数是否已经到达上限
                    if (sequence == 0)
                    {
                        //一微妙内产生的ID计数已达上限,等待下一微妙
                        timestamp = GetNextTimestamp(lastTimestamp);
                    }
                }
                else
                {
                    //不同微秒生成ID
                    sequence = 0L;
                }
                if (timestamp < lastTimestamp)
                {
                    throw new Exception("时间戳比上一次生成ID时时间戳还小,故异常");
                }
                Snowflake.lastTimestamp = timestamp; //把当前时间戳保存为最后生成ID的时间戳
                long Id = ((timestamp - twepoch) << (int)timestampLeftShift)
                    | (datacenterId << (int)datacenterIdShift)
                    | (machineId << (int)machineIdShift)
                    | sequence;
                return Id;
            }
        }
    }
}

2、添加一个entity类:

/*
*创建人:sophia
*创建时间:2019/2/27 19:43:16
*说明:
*版权所有:温娉哲
*/
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;

namespace snowflakeTest
{
    class entity
    {
        private string text;
        public string  Text
        {
            get;set;
        }

    }
}

3、测试一下雪花算法生成结果,打开program.cs,在主函数中让其循环输出到20个:

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using using和layout;
using snowflakeTest;

namespace snowflakeTest
{
    class Program
    {
        static void Main(string[] args)
        {
            Snowflake snowflake = new Snowflake();
            entity rtxtMes = new entity();

            for (int i = 0; i < 20; i++)
            {
                rtxtMes.Text += "开始执行 " + DateTime.Now.ToString("yyyy-MM-dd HH:mm:ss:ffffff") + "    " + Snowflake.Instance().GetId() + "\n";
                Console.WriteLine(rtxtMes.Text);
            }
            Console.ReadKey();
            
        }
    }
}

测试输出结果。可以看到快速执行的时候时间的毫秒会出现相同的情况,但是snowflake生产了唯一的ID。

雪花算法_数据结构与算法_03

PS:雪花算法实现产生递增而不重复的ID,使用UUID或者GUID产生的ID没有规则。