java中雪花算法時鐘回?fù)軉栴}解決
雪花算法(Snowflake)是Twitter開源的一種分布式唯一ID生成算法,用于在分布式系統(tǒng)中生成全局唯一的ID。雪花算法生成的ID是一個64位的整數(shù),通常表示為長整型(long)。
雪花算法的結(jié)構(gòu)
雪花算法生成的ID由以下幾部分組成:
- 符號位(1位):始終為0,保證生成的ID為正數(shù)。
- 時間戳(41位):記錄生成ID的時間戳,精確到毫秒級。可以使用大約69年的時間。
- 機器ID(10位):標(biāo)識生成ID的機器,可以支持1024臺機器。
- 序列號(12位):同一毫秒內(nèi)生成的多個ID的序列號,可以支持每毫秒生成4096個ID。
時鐘回?fù)軉栴}
時鐘回?fù)軉栴}是指在分布式系統(tǒng)中,由于各種原因(如NTP時間同步),某些節(jié)點的系統(tǒng)時間可能會回退到過去的時間點。這會導(dǎo)致雪花算法生成的ID出現(xiàn)重復(fù),因為時間戳部分會重復(fù)。
解決時鐘回?fù)軉栴}的方法
等待機制:
- 當(dāng)檢測到時鐘回?fù)軙r,生成器可以等待時間追上上次生成ID的時間戳,然后再生成新的ID。這種方法簡單直接,但可能會導(dǎo)致生成器在等待期間無法生成新的ID。
擴展位:
- 在ID結(jié)構(gòu)中增加額外的位來處理時鐘回?fù)?。例如,可以使用額外的位來記錄時鐘回?fù)艿拇螖?shù),從而避免ID重復(fù)。
預(yù)留時間戳:
- 在生成ID時,預(yù)留一些時間戳范圍,用于處理時鐘回?fù)?。例如,可以預(yù)留一些時間戳范圍,當(dāng)檢測到時鐘回?fù)軙r,使用預(yù)留的時間戳生成新的ID。
邏輯時鐘:
- 使用邏輯時鐘(如Lamport時鐘或Vector時鐘)代替物理時鐘。邏輯時鐘可以保證在分布式系統(tǒng)中事件的順序,避免時鐘回?fù)軉栴}。
示例代碼
以下是一個使用等待機制解決時鐘回?fù)軉栴}的雪花算法實現(xiàn)示例:
public class SnowflakeIdGenerator {
private final long twepoch = 1288834974657L; // 起始時間戳,例如Twitter的Snowflake起始時間
private final long workerIdBits = 10L;
private final long maxWorkerId = -1L ^ (-1L << workerIdBits);
private final long sequenceBits = 12L;
private final long workerIdShift = sequenceBits;
private final long timestampLeftShift = sequenceBits + workerIdBits;
private final long sequenceMask = -1L ^ (-1L << sequenceBits);
private long workerId;
private long sequence = 0L;
private long lastTimestamp = -1L;
public SnowflakeIdGenerator(long workerId) {
if (workerId > maxWorkerId || workerId < 0) {
throw new IllegalArgumentException(String.format("worker Id can't be greater than %d or less than 0", maxWorkerId));
}
this.workerId = workerId;
}
public synchronized long nextId() {
long timestamp = timeGen();
if (timestamp < lastTimestamp) {
// 時鐘回?fù)?,等待時間追上
long offset = lastTimestamp - timestamp;
if (offset <= 5) {
try {
wait(offset << 1);
timestamp = timeGen();
if (timestamp < lastTimestamp) {
throw new RuntimeException(String.format("Clock moved backwards. Refusing to generate id for %d milliseconds", offset));
}
} catch (InterruptedException e) {
throw new RuntimeException(e);
}
} else {
throw new RuntimeException(String.format("Clock moved backwards. Refusing to generate id for %d milliseconds", offset));
}
}
if (lastTimestamp == timestamp) {
sequence = (sequence + 1) & sequenceMask;
if (sequence == 0) {
timestamp = tilNextMillis(lastTimestamp);
}
} else {
sequence = 0L;
}
lastTimestamp = timestamp;
return ((timestamp - twepoch) << timestampLeftShift) |
(workerId << workerIdShift) |
sequence;
}
protected long tilNextMillis(long lastTimestamp) {
long timestamp = timeGen();
while (timestamp <= lastTimestamp) {
timestamp = timeGen();
}
return timestamp;
}
protected long timeGen() {
return System.currentTimeMillis();
}
public static void main(String[] args) {
SnowflakeIdGenerator idGenerator = new SnowflakeIdGenerator(1);
System.out.println(idGenerator.nextId());
}
}
代碼解釋
檢測時鐘回?fù)?/strong>:
- 在生成ID時,首先獲取當(dāng)前時間戳,并與上次生成ID的時間戳進行比較。
- 如果當(dāng)前時間戳小于上次生成ID的時間戳,說明發(fā)生了時鐘回?fù)堋?/li>
等待機制:
- 如果時鐘回?fù)艿臅r間差小于等于5毫秒,生成器會等待時間追上上次生成ID的時間戳。
- 如果時鐘回?fù)艿臅r間差大于5毫秒,拋出異常,拒絕生成ID。
生成新的ID:
- 如果時鐘沒有回?fù)?,或者等待時間追上后,生成新的ID。
總結(jié)
時鐘回?fù)軉栴}是分布式系統(tǒng)中使用雪花算法生成唯一ID時需要解決的一個重要問題。通過使用等待機制、擴展位、預(yù)留時間戳或邏輯時鐘等方法,可以有效避免時鐘回?fù)軐?dǎo)致的ID重復(fù)問題。在實際應(yīng)用中,可以根據(jù)具體需求選擇合適的解決方案。
到此這篇關(guān)于java中雪花算法時鐘回?fù)軉栴}解決的文章就介紹到這了,更多相關(guān)java 雪花算法時鐘回?fù)軆?nèi)容請搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!
相關(guān)文章
SpringBoot集成xxl-job實現(xiàn)超牛的定時任務(wù)的步驟詳解
XXL-JOB是一個分布式任務(wù)調(diào)度平臺,其核心設(shè)計目標(biāo)是開發(fā)迅速、學(xué)習(xí)簡單、輕量級、易擴展,現(xiàn)已開放源代碼并接入多家公司線上產(chǎn)品線,開箱即用,本文給大家介紹了SpringBoot集成xxl-job實現(xiàn)超牛的定時任務(wù),需要的朋友可以參考下2023-10-10
Java 實戰(zhàn)項目之誠途旅游系統(tǒng)的實現(xiàn)流程
讀萬卷書不如行萬里路,只學(xué)書上的理論是遠遠不夠的,只有在實戰(zhàn)中才能獲得能力的提升,本篇文章手把手帶你用java+SpringBoot+Vue+maven+Mysql實現(xiàn)一個精美的物流管理系統(tǒng),大家可以在過程中查缺補漏,提升水平2021-11-11

