分步式系统中全局唯一主键

 

/// <summary>
    /// 分步式全局唯一Id生成算法
    /// 根据SnowFlake算法生成全局唯一Id.(由时间戳,区域位,站点位,随机数四部分组成)
    /// </summary>
    public static class GlobalUniqueId
    {
        /// <summary>
        ////// </summary>
        private static readonly Object LockObj = new Object();

        /// <summary>
        /// 纪元开始
        /// </summary>
        private static readonly DateTime BaseDate = new DateTime(2016, 1, 1, 0, 0, 0);

        /// <summary>
        /// 22~62位(从低位到高位 0~63位)共41个位用来记录时间戳(毫秒)
        /// </summary>
        private static readonly int MillscondsBits = 41;

        /// <summary>
        /// 17~21位(从低位到高位 0~63位)共5个位用来记录区域id。
        /// </summary>
        private static readonly int DatacenterIdBits = 5;

        /// <summary>
        /// 12~16位(从低位到高位 0~63位)共5个位用来记录工作机器id。
        /// </summary>
        private static readonly int WorkerIdBits = 5;

        /// <summary>
        /// 0~11位(从低位到高位 0~63位)共12个位用来记录序列号(同毫秒内产生的不同id)。
        /// </summary>
        private static readonly int SequenceBits = 12;

        /// <summary>
        /// 最大区域Id
        /// </summary>
        private static readonly int MaxDataCenterId = (1 << DatacenterIdBits) - 1;
        /// <summary>
        /// 最大站点Id
        /// </summary>
        private static readonly int MaxWorkerId = (1 << WorkerIdBits) - 1;

        /// <summary>
        /// 时间戳左移位
        /// </summary>
        private static readonly int TimestampLeftShift = SequenceBits + WorkerIdBits + DatacenterIdBits;

        /// <summary>
        /// 区域左移位
        /// </summary>
        private static readonly int DatacenterIdLeftShift = SequenceBits + WorkerIdBits;

        /// <summary>
        /// 站点左移位
        /// </summary>
        private static readonly int WorkerIdLeftShift = SequenceBits;

        /// <summary>
        /// 最大序列号 
        /// </summary>
        private static readonly int SequenceMask = (1 << SequenceBits) - 1; //4095

        /// <summary>
        /// 当前序列号
        /// </summary>
        private static int _sequence;

        /// <summary>
        /// 上次时间戳
        /// </summary>
        private static long _lastTimestamp = -1L;

        /// <summary>
        /// 获取时间戳
        /// </summary>
        /// <returns></returns>
        private static long GetMillSeconds()
        {
            var ts = DateTime.Now - BaseDate;
            return (long)ts.TotalMilliseconds;
        }

        private static long NextMillis(long lastTimestamp)
        {
            long timestamp = GetMillSeconds();
            while (timestamp <= lastTimestamp)
            {
                timestamp = GetMillSeconds();
            }
            return timestamp;
        }

        /// <summary>
        /// 计算 当前时间毫秒位 与 当前时间的序列位
        /// </summary>
        /// <returns></returns>
        private static long GenerateTimestampAndSequence(ref int sequence)
        {
            long timestamp = GetMillSeconds();

            if (timestamp < _lastTimestamp)
            {
                throw new Exception(
                    String.Format("Clock moved backwards.  Refusing to generate id for {0} milliseconds",
                        _lastTimestamp - timestamp));
            }

            if (_lastTimestamp == timestamp)
            {
                sequence = (sequence + 1) & SequenceMask;
                if (sequence == 0)
                {
                    timestamp = NextMillis(_lastTimestamp);
                }
            }
            else
            {
                sequence = 0;
            }

            _lastTimestamp = timestamp;
            _sequence = sequence;

            return timestamp;
        }

        /// <summary>
        /// 根据SnowFlake算法生成全局唯一Id.(由时间戳,区域位,站点位,序列数四部分组成)
        /// </summary>
        /// <param name="dataCenterId"></param>
        /// <param name="workerId"></param>
        /// <returns></returns>
        public static long NextId(byte dataCenterId, ushort workerId)
        {
            if (dataCenterId >= MaxDataCenterId)
            {
                throw new Exception("区域号超过最大值");
            }

            if (workerId >= MaxWorkerId)
            {
                throw new Exception("机器号超过最大值");
            }

            int sequence = 0;
            long ms = 0;
            lock (LockObj)
            {
                //计算 当前时间毫秒位 与 当前时间的序列位
                sequence = _sequence;
                ms = GenerateTimestampAndSequence(ref sequence);
            }

            return (ms << TimestampLeftShift) //生成 当前时间毫秒位 
                   | (dataCenterId << DatacenterIdLeftShift) //生成区域位
                   | (workerId << WorkerIdLeftShift) //生成站点位
                   | sequence; //生成序列位
        }

        /// <summary>
        /// 默认分布式Id生成算法生成的全局唯一值
        /// </summary>
        /// <returns></returns>
        public static long NextId()
        {
            return NextId(1, 1);
        }
    }

 

 

 

 

 

 

前几天看到一篇Java版的全局唯一主键,觉得写的很不错,就直接简化了成C#,

long共 64个字节

其中0-9是随机数,10-19是计算机唯一值,20-22是区域唯一值,23-63是时间毫秒数

这样 随机数小于 2^10=1024

计算机唯一值 小于 2^10=1024

区域唯一值 小于 2^3=8

毫秒数 2^41,可以用上 99年(感觉有点少啊)

 

class GlobalUniqueId
{
const int MaxRegionId = 8;
const int MaxComputerId = 1024;
private long _regionId;
private long _computerId;
static GlobalUniqueId _Instance = null;
private GlobalUniqueId()
{
}
static object objLock = new object();
public static GlobalUniqueId Instance
{
get
{
if (_Instance == null)
{
lock (objLock)
{
if (_Instance == null)
_Instance = new GlobalUniqueId();
}
}
return _Instance;
}
}
static readonly DateTime BASE_DATE = new DateTime(2010, 1, 1, 0, 0, 0);

long getMillSeconds()
{
TimeSpan ts = DateTime.Now - BASE_DATE;
return (long)ts.TotalMilliseconds;
}
void addRegionId(ref long r)
{

r = r | (_regionId << (64 - 41 - 3));

}
void addComputerId(ref long r)
{

r = r | (_computerId << (64 - 41 - 3 - 10));

}
void addMillSecondId(ref long r)
{
long ms = getMillSeconds();
r = r | (ms << (64 - 41));

}
static readonly Random rand = new Random();
static readonly int MaxRandId = (int)(Math.Pow(2, 10));
void addRandomId(ref long r)
{
long rndId = rand.Next(0, MaxRandId);

r = r | rndId;

}
public long NextId(int regionId, int computerId)
{
if (regionId >= MaxRegionId) throw new Exception("区域号超过最大值");
if (computerId >= MaxComputerId) throw new Exception("机器号超过最大值");
this._regionId = regionId;
this._computerId = computerId;
long r = 0;//
addMillSecondId(ref r);
addRegionId(ref r);
addComputerId(ref r);
addRandomId(ref r);
return r;
}
}

posted @ 2016-04-08 22:30  zslm___  阅读(398)  评论(0编辑  收藏  举报