SnowFlake算法生成id的结果是一个64bit大小的整数,它的结构如下图:
图片描述
1位,不用。二进制中最高位为1的都是负数,但是我们生成的id一般都使用整数,所以这个最高位固定是0
41位,用来记录时间戳(毫秒)。
41位可以表示241−12^{41}-1241−1个数字,
如果只用来表示正整数(计算机中正数包含0),可以表示的数值范围是:0 至 241−12^{41}-1241−1,减1是因为可表示的数值范围是从0开始算的,而不是1。
也就是说41位可以表示241−12^{41}-1241−1个毫秒的值,转化成单位年则是(241−1)/(1000∗60∗60∗24∗365)=69(2^{41}-1) / (1000 * 60 * 60 * 24 * 365) = 69(241−1)/(1000∗60∗60∗24∗365)=69年
10位,用来记录工作机器id。
可以部署在210=10242^{10} = 1024210=1024个节点,包括5位datacenterId和5位workerId
5位(bit)可以表示的最大正整数是25−1=312^{5}-1 = 3125−1=31,即可以用0、1、2、3、…31这32个数字,来表示不同的datecenterId或workerId
12位,序列号,用来记录同毫秒内产生的不同id。
12位(bit)可以表示的最大正整数是212−1=40952^{12}-1 = 4095212−1=4095,即可以用0、1、2、3、…4094这4095个数字,来表示同一机器同一时间截(毫秒)内产生的4095个ID序号
由于在Java中64bit的整数是long类型,所以在Java中SnowFlake算法生成的id就是long来存储的。
SnowFlake可以保证:
所有生成的id按时间趋势递增
整个分布式系统内不会产生重复id(因为有datacenterId和workerId来做区分)
private static long datacenterId=0L;//数据中心
private static long machineId;//机器id
private static long sequence = 0L;//计数0开始
private static long lastTimestamp = -1L;//最后时间戳
private static long twepoch = 687888001020L;//唯一时间随机量
private static long machineIdBits = 5L;//机器码字节数
private static long datacenterIdBits = 5L;//数据字节
private 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 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 manchineId, long datacenterId)
{
Snowflakes(manchineId, datacenterId);
}
private void Snowflakes(long machineld, long datacenterid)
{
if (machineId>=0)
{
if (machineId>maxMachineId)
{
throw new Exception("机器码DI非法");
}
Snowflake.maxMachineId = machineId;
}
if (datacenterid>=0)
{
if (datacenterid>maxDatacenterId)
{
throw new Exception("数据中心ID非法");
}
Snowflake.datacenterId = datacenterId;
}
}
/// <summary>
/// 生成当前事件戳
/// </summary>
/// <returns>毫秒returns>
private static long GetTimestamp()
{
//从2000年开始
return (long)(DateTime.UtcNow - new DateTime(2000, 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;
}
public static 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 Exception("时间戳大小异常!");
}
Snowflake.lastTimestamp = timestamp;//把当前时间戳保存为最后生成ID的时间戳
long Id = ((timestamp - twepoch)) << (int)timestampLeftShift
| (datacenterId << (int)datacenterIdShift)
| (machineId << (int)machineIdShift)
| sequence;
return Id;
}
}