Created
April 24, 2019 07:21
-
-
Save rqx110/5a16dfaeee0a289bb01ad9a3c3fd5f87 to your computer and use it in GitHub Desktop.
分布式ID自增算法 Snowflake
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
using System; | |
namespace XCode.Common | |
{ | |
#region 算法1 | |
///// <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 XCode.PowerException("机器码{0}非法".CC().F("ID")); | |
// } | |
// Snowflake.machineId = machineId; | |
// } | |
// if (datacenterId >= 0) | |
// { | |
// if (datacenterId > maxDatacenterId) | |
// { | |
// throw new XCode.PowerException("数据中心{0}非法".CC().F("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(Snowflake.lastTimestamp); | |
// } | |
// } | |
// else | |
// { | |
// //不同微秒生成ID | |
// sequence = 0L; | |
// } | |
// if (timestamp < lastTimestamp) | |
// { | |
// throw new XCode.PowerException("时间戳比上一次生成{0}时时间戳还小,故异常".CC().F("ID")); | |
// } | |
// Snowflake.lastTimestamp = timestamp; //把当前时间戳保存为最后生成ID的时间戳 | |
// long Id = ((timestamp - twepoch) << (int)timestampLeftShift) | |
// | (datacenterId << (int)datacenterIdShift) | |
// | (machineId << (int)machineIdShift) | |
// | sequence; | |
// return Id; | |
// } | |
// } | |
//} | |
#endregion | |
#region 算法2 | |
/// <summary> | |
/// http://www.cnblogs.com/kasimlz/p/7511131.html | |
/// </summary> | |
public class Snowflake | |
{ | |
/// <summary> | |
/// 机器码 | |
/// </summary> | |
private static long _workerId; | |
/// <summary> | |
/// 初始基准时间戳,小于当前时间点即可 | |
/// 分布式项目请保持此时间戳一致 | |
/// </summary> | |
private static long _twepoch = 0L; | |
/// <summary> | |
/// 毫秒计数器 | |
/// </summary> | |
private static long sequence = 0L; | |
/// <summary> | |
/// 机器码字节数。4个字节用来保存机器码(定义为Long类型会出现,最大偏移64位,所以左移64位没有意义) | |
/// </summary> | |
private static int workerIdBits = 4; | |
/// <summary> | |
/// 最大机器ID所占的位数 | |
/// </summary> | |
private static long maxWorkerId = -1L ^ -1L << workerIdBits; | |
/// <summary> | |
/// 计数器字节数,10个字节用来保存计数码 | |
/// </summary> | |
private static int sequenceBits = 12; | |
/// <summary> | |
/// 机器码数据左移位数,就是后面计数器占用的位数 | |
/// </summary> | |
private static int workerIdShift = sequenceBits; | |
/// <summary> | |
/// 时间戳左移动位数就是机器码和计数器总字节数 | |
/// </summary> | |
private static int timestampLeftShift = sequenceBits + workerIdBits; | |
/// <summary> | |
/// 一微秒内可以产生计数,如果达到该值则等到下一微妙在进行生成 | |
/// </summary> | |
private static long sequenceMask = -1L ^ -1L << sequenceBits; | |
/// <summary> | |
/// 最后一次的时间戳 | |
/// </summary> | |
private static long lastTimestamp = -1L; | |
/// <summary> | |
/// 线程锁对象 | |
/// </summary> | |
private static object locker = new object(); | |
static Snowflake() | |
{ | |
_workerId = new Random(DateTime.Now.Millisecond).Next(1, (int)maxWorkerId); | |
_twepoch = timeGen(2010, 1, 1, 0, 0, 0); | |
} | |
/// <summary> | |
/// 机器编号 | |
/// </summary> | |
public static long WorkerID | |
{ | |
get { return _workerId; } | |
set | |
{ | |
if (value > 0 && value < maxWorkerId) | |
_workerId = value; | |
else | |
throw new XCode.PowerException("{0}0{1}{2}".CC().F("Workeridmustbegreaterthan","orlessthan",maxWorkerId)); | |
} | |
} | |
/// <summary> | |
/// 获取新的ID | |
/// </summary> | |
/// <returns></returns> | |
public static long NewID() | |
{ | |
lock (locker) | |
{ | |
long timestamp = timeGen(); | |
if (lastTimestamp == timestamp) | |
{ //同一微妙中生成ID | |
sequence = (sequence + 1) & sequenceMask; //用&运算计算该微秒内产生的计数是否已经到达上限 | |
if (sequence == 0) | |
{ | |
//一微妙内产生的ID计数已达上限,等待下一微妙 | |
timestamp = tillNextMillis(lastTimestamp); | |
} | |
} | |
else | |
{ //不同微秒生成ID | |
sequence = 0; //计数清0 | |
} | |
if (timestamp < lastTimestamp) | |
{ | |
//如果当前时间戳比上一次生成ID时时间戳还小,抛出异常,因为不能保证现在生成的ID之前没有生成过 | |
throw new XCode.PowerException(string.Format("Clock moved backwards. Refusing to generate id for {0} milliseconds", lastTimestamp - timestamp)); | |
} | |
lastTimestamp = timestamp; //把当前时间戳保存为最后生成ID的时间戳 | |
return (timestamp - _twepoch << timestampLeftShift) | _workerId << workerIdShift | sequence; | |
} | |
} | |
/// <summary> | |
/// 获取下一微秒时间戳 | |
/// </summary> | |
/// <param name="lastTimestamp"></param> | |
/// <returns></returns> | |
private static long tillNextMillis(long lastTimestamp) | |
{ | |
long timestamp = timeGen(); | |
while (timestamp <= lastTimestamp) | |
{ | |
timestamp = timeGen(); | |
} | |
return timestamp; | |
} | |
/// <summary> | |
/// 当前时间戳 | |
/// </summary> | |
/// <returns></returns> | |
private static long timeGen() | |
{ | |
return (long)(DateTime.UtcNow - new DateTime(1970, 1, 1, 0, 0, 0, DateTimeKind.Utc)).TotalMilliseconds; | |
} | |
/// <summary> | |
/// 指定时间戳 | |
/// </summary> | |
/// <param name="Year"></param> | |
/// <param name="Month"></param> | |
/// <param name="Day"></param> | |
/// <param name="Hour"></param> | |
/// <param name="Minute"></param> | |
/// <param name="Second"></param> | |
/// <returns></returns> | |
private static long timeGen(int Year, int Month, int Day, int Hour, int Minute, int Second) | |
{ | |
var UtcTime = new DateTime(Year, Month, Day, Hour, Minute, Second, DateTimeKind.Utc); | |
return (long)(UtcTime - new DateTime(1970, 1, 1, 0, 0, 0, DateTimeKind.Utc)).TotalMilliseconds; | |
} | |
} | |
#endregion | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment