Twitter的snowflake算法是在分布式系統(tǒng)中一種自增ID的算法,ID能夠按照時(shí)間有序生成并且可以做到全局唯一衙熔。
算法生成的是Long類型的id赶撰,一個(gè)Long類型占8個(gè)字節(jié)蝌以,每個(gè)字節(jié)占8比特,也就是說一個(gè)Long類型占64個(gè)比特(0和1)栋荸。
Twitter是這樣分配的:正數(shù)位(占1比特)+時(shí)間戳(占41比特)+機(jī)器id(占5比特)+數(shù)據(jù)中心(占5比特)+自增值(占12比特)菇怀,總共64比特組成的一個(gè)Long類型。
- 正數(shù)位(占1比特):由于long基本類型在Java中是帶符號(hào)的晌块,最高位是符號(hào)位爱沟,正數(shù)是0,負(fù)數(shù)是1匆背,由于id一般是正數(shù)呼伸,所以這個(gè)位一般都是0
- 時(shí)間戳(占41個(gè)比特):毫秒數(shù),大約可以使使用69年
- 機(jī)器id(占5個(gè)比特):即2的5次方等于32個(gè)機(jī)器
- 數(shù)據(jù)中心id(占5個(gè)比特):即2的5次方等于32個(gè)數(shù)據(jù)中心
- 自增值(占12比特):2的12次方等于4096钝尸。也就是說每毫秒最多可以生成4096個(gè)id括享,如果cpu生產(chǎn)id的速度大于每毫秒4096個(gè)搂根,那么需要使線程進(jìn)行等待到下一毫秒,重新計(jì)數(shù)獲取自增值铃辖。
SnowFlake的優(yōu)點(diǎn)是剩愧,整體上按照時(shí)間自增排序,并且整個(gè)分布式系統(tǒng)內(nèi)不會(huì)產(chǎn)生ID碰撞(由數(shù)據(jù)中心ID和機(jī)器ID作區(qū)分)娇斩,并且效率較高仁卷,經(jīng)測試,SnowFlake每秒能夠產(chǎn)生26萬ID左右犬第。
snowflake算法有一個(gè)弊端锦积,每毫秒重新計(jì)數(shù),空閑時(shí)間會(huì)浪費(fèi)很多id空間瓶殃。針對空閑時(shí)間會(huì)浪費(fèi)很多id空間的改進(jìn)辦法:咱們可以把時(shí)間戳的單位改為秒。使用31個(gè)比特的時(shí)間戳(秒)副签,節(jié)約了10個(gè)比特遥椿,2的31次方等于2,147,483,648秒,約為69年淆储。然后我們把節(jié)約出來的10個(gè)比特交給自增值冠场,此時(shí)自增值(12+10=22比特),即2的22次方等于4,194,304本砰。
改進(jìn)前的snowflake算法結(jié)構(gòu)為:正數(shù)位(占1比特)+時(shí)間戳(占41比特)+機(jī)器id(占5比特)+數(shù)據(jù)中心id(占5比特)+自增值(占12比特)
改進(jìn)后的snowflake算法結(jié)構(gòu)為:正數(shù)位(占1比特)+時(shí)間戳(占31比特)+機(jī)器id(占5比特)+數(shù)據(jù)中心id(占5比特)+自增值(占22比特)
改進(jìn)后的優(yōu)點(diǎn):避免空閑時(shí)間會(huì)浪費(fèi)很多id空間碴裙,支持每秒生成419萬個(gè)id。
改進(jìn)后的實(shí)現(xiàn)代碼如下:
public class SnowflakeIdWorker2nd {
/** 開始時(shí)間截 (2019-01-01) */
private final long twepoch = 1546272000000L;
/** 機(jī)器id所占的位數(shù) */
private final long workerIdBits = 5L;
/** 數(shù)據(jù)標(biāo)識(shí)id所占的位數(shù) */
private final long datacenterIdBits = 5L;
/** 支持的最大機(jī)器id点额,結(jié)果是31 (這個(gè)移位算法可以很快的計(jì)算出幾位二進(jìn)制數(shù)所能表示的最大十進(jìn)制數(shù)) */
private final long maxWorkerId = -1L ^ (-1L << workerIdBits);
/** 支持的最大數(shù)據(jù)標(biāo)識(shí)id舔株,結(jié)果是31 */
private final long maxDatacenterId = -1L ^ (-1L << datacenterIdBits);
/** 序列在id中占的位數(shù) */
private final long sequenceBits = 22L;
/** 機(jī)器ID向左移22位 */
private final long workerIdShift = sequenceBits;
/** 數(shù)據(jù)標(biāo)識(shí)id向左移27位(22+5) */
private final long datacenterIdShift = sequenceBits + workerIdBits;
/** 時(shí)間截向左移32位(5+5+22) */
private final long timestampLeftShift = sequenceBits + workerIdBits
+ datacenterIdBits;
/** 生成序列的掩碼,這里為4194303 */
private final long sequenceMask = -1L ^ (-1L << sequenceBits);
/** 工作機(jī)器ID(0~31) */
private long workerId;
/** 數(shù)據(jù)中心ID(0~31) */
private long datacenterId;
/** 秒內(nèi)序列(0~4194303) */
private long sequence = 0L;
/** 上次生成ID的時(shí)間截 */
private int lastTimestamp = -1;
// ==============================Constructors=====================================
/**
* 構(gòu)造函數(shù)
*
* @param workerId
* 工作ID (0~31)
* @param datacenterId
* 數(shù)據(jù)中心ID (0~31)
*/
public SnowflakeIdWorker2nd(long workerId, long datacenterId) {
if (workerId > maxWorkerId || workerId < 0) {
throw new IllegalArgumentException(String.format(
"worker Id can't be greater than %d or less than 0",
maxWorkerId));
}
if (datacenterId > maxDatacenterId || datacenterId < 0) {
throw new IllegalArgumentException(String.format(
"datacenter Id can't be greater than %d or less than 0",
maxDatacenterId));
}
this.workerId = workerId;
this.datacenterId = datacenterId;
}
// ==============================Methods==========================================
/**
* 獲得下一個(gè)ID (該方法是線程安全的)
*
* @return SnowflakeId
*/
public synchronized long nextId() {
int timestamp = timeGen();
// 如果當(dāng)前時(shí)間小于上一次ID生成的時(shí)間戳还棱,說明系統(tǒng)時(shí)鐘回退過這個(gè)時(shí)候應(yīng)當(dāng)拋出異常
if (timestamp < lastTimestamp) {
throw new RuntimeException(String.format(
"Clock moved backwards. Refusing to generate id for %d milliseconds",
lastTimestamp - timestamp));
}
// 如果是同一時(shí)間生成的载慈,則進(jìn)行秒內(nèi)序列
if (lastTimestamp == timestamp) {
sequence = (sequence + 1) & sequenceMask;
// 秒內(nèi)序列溢出
if (sequence == 0) {
// 阻塞到下一個(gè)秒,獲得新的時(shí)間戳
timestamp = tilNextMillis(lastTimestamp);
}
}
// 時(shí)間戳改變,秒內(nèi)序列重置
else {
sequence = 0L;
}
// 上次生成ID的時(shí)間截
lastTimestamp = timestamp;
// 移位并通過或運(yùn)算拼到一起組成64位的ID
return ((timestamp - twepoch) << timestampLeftShift) //
| (datacenterId << datacenterIdShift) //
| (workerId << workerIdShift) //
| sequence;
}
/**
* 阻塞到下一個(gè)秒珍手,直到獲得新的時(shí)間戳
*
* @param lastTimestamp
* 上次生成ID的時(shí)間截
* @return 當(dāng)前時(shí)間戳
*/
protected int tilNextMillis(int lastTimestamp) {
int timestamp = timeGen();
while (timestamp <= lastTimestamp) {
timestamp = timeGen();
}
return timestamp;
}
/**
* 返回以秒為單位的當(dāng)前時(shí)間
*
* @return 當(dāng)前時(shí)間(秒)
*/
protected int timeGen() {
String timestamp = String.valueOf(System.currentTimeMillis() / 1000);
return Integer.valueOf(timestamp);
}
// ==============================Test=============================================
/** 測試 */
public static void main(String[] args) {
SnowflakeIdWorker2nd idWorker = new SnowflakeIdWorker2nd(0, 0);
for (int i = 0; i < 1000; i++) {
long id = idWorker.nextId();
System.out.println(Long.toBinaryString(id));//轉(zhuǎn)為Bit办铡,前面的0省略掉
System.out.println(id);
}
}
}