分步式系统中全局唯一主键
/// <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;
}
}