一、概念
❄ 什麼是雪花算法
SnowFlake算法是Twitter公司出品的開源的分佈式id生成算法
其特點爲 使用一個64 bit的long型的數字作爲全局唯一 id
雪花算法在分佈式系統中的應用十分廣泛 且引入了時間戳 基本保持自增的
❄ 雪花算法字符串各部分的含義
- 第
1位
是符號位 始終爲0
(這是因爲生成的id都是正數 而在二進制中第一個bit若爲0則不爲負數) - 後面是
41位
的時間戳 精確到毫秒級
41位的長度可以表示2^41-1個毫秒值 也就是說可以使用69年
時間戳還有一個很重要的作用 可以根據時間進行排序 - 之後的
10位
是機器標識 前5bit是機房id 後5bit是機器id
10位的長度表明該服務最多可以部署在2^10臺機器(即1024臺機器)上 - 最後
12位
是計數序列號
序列號是一系列的自增id 表示了同一個毫秒內產生的不同id
可以支持同一節點同一毫秒生成多個id 12位的計數序列號支持每個節點每毫秒產生2^12-1(即4096)個ID序號
❄ 生成過程
- 1、若某個服務需要生成一個唯一id 則發送一個請求給部署了SnowFlake算法的系統(前提是該SnowFlake算法系統知道自己所在的機房和機器的編號)
- 2、SnowFlake算法系統接收到該請求後 使用二進制位運算的方式生成一個64bit的long型id 當然 第一個bit是無意義的
- 3、接着41個bit使用當前時間戳(單位爲毫秒) 然後的5bit設爲該機房的id 剩餘5bit設爲機器的id
- 4、最後 再判斷當前機房的該機器在這一毫秒內是第幾個請求 給本次生成id的請求後再累加一個序號 作爲id最後的12個bit
至此 就得到了一個64bit的唯一id 這就是雪花算法
二、代碼實現
工具類:
public class SnowFlakeUtil {
// 起始時間戳
private final static long START_STMP = 1480166465631L;
// 每部分的位數
private final static long SEQUENCE_BIT = 12; // 序列號佔用位數
private final static long MACHINE_BIT = 5; // 機器id佔用位數
private final static long DATACENTER_BIT = 5; // 機房id佔用位數
// 每部分最大值
private final static long MAX_DATACENTER_NUM = -1L ^ (-1L << DATACENTER_BIT);
private final static long MAX_MACHINE_NUM = -1L ^ (-1L << MACHINE_BIT);
private final static long MAX_SEQUENCE = -1L ^ (-1L << SEQUENCE_BIT);
// 每部分向左的位移
private final static long MACHINE_LEFT = SEQUENCE_BIT;
private final static long DATACENTER_LEFT = SEQUENCE_BIT + MACHINE_BIT;
private final static long TIMESTMP_LEFT = DATACENTER_LEFT + DATACENTER_BIT;
private long datacenterId; // 機房id
private long machineId; // 機器id
private long sequence = 0L; // 序列號
private long lastStmp = -1L; // 上次的時間戳
public SnowFlakeUtil(long datacenterId, long machineId) {
if (datacenterId > MAX_DATACENTER_NUM || datacenterId < 0)
{
throw new IllegalArgumentException("datacenterId can't be greater than MAX_DATACENTER_NUM or less than 0");
}
if (machineId > MAX_MACHINE_NUM || machineId < 0)
{
throw new IllegalArgumentException("machineId can't be greater than MAX_MACHINE_NUM or less than 0");
}
this.datacenterId = datacenterId;
this.machineId = machineId;
}
// 產生下一個ID
public synchronized long getNextId() {
long currStmp = getNewstmp();
if (currStmp < lastStmp) {
throw new RuntimeException("Clock moved backwards.Refusing to generate id");
}
if (currStmp == lastStmp) {
// 若在相同毫秒內 序列號自增
sequence = (sequence + 1) & MAX_SEQUENCE;
// 同一毫秒的序列數已達到最大
if (sequence == 0L)
{
currStmp = getNextMill();
}
} else {
// 若在不同毫秒內 則序列號置爲0
sequence = 0L;
}
lastStmp = currStmp;
return (currStmp - START_STMP) << TIMESTMP_LEFT // 時間戳部分
| datacenterId << DATACENTER_LEFT // 機房id部分
| machineId << MACHINE_LEFT // 機器id部分
| sequence; // 序列號部分
}
// 獲取新的毫秒數
private long getNextMill()
{
long mill = getNewstmp();
while (mill <= lastStmp)
{
mill = getNewstmp();
}
return mill;
}
// 獲取當前的毫秒數
private long getNewstmp()
{
return System.currentTimeMillis();
}
}
使用:
SnowFlakeUtil snowFlakeUtil = new SnowFlakeSnowFlakeUtil (12,13);
System.out.println(snowFlakeUtil.nextId());