java中雪花算法時(shí)鐘回?fù)軉栴}解決
雪花算法(Snowflake)是Twitter開源的一種分布式唯一ID生成算法,用于在分布式系統(tǒng)中生成全局唯一的ID。雪花算法生成的ID是一個(gè)64位的整數(shù),通常表示為長(zhǎng)整型(long)。
雪花算法的結(jié)構(gòu)
雪花算法生成的ID由以下幾部分組成:
- 符號(hào)位(1位):始終為0,保證生成的ID為正數(shù)。
- 時(shí)間戳(41位):記錄生成ID的時(shí)間戳,精確到毫秒級(jí)??梢允褂么蠹s69年的時(shí)間。
- 機(jī)器ID(10位):標(biāo)識(shí)生成ID的機(jī)器,可以支持1024臺(tái)機(jī)器。
- 序列號(hào)(12位):同一毫秒內(nèi)生成的多個(gè)ID的序列號(hào),可以支持每毫秒生成4096個(gè)ID。
時(shí)鐘回?fù)軉栴}
時(shí)鐘回?fù)軉栴}是指在分布式系統(tǒng)中,由于各種原因(如NTP時(shí)間同步),某些節(jié)點(diǎn)的系統(tǒng)時(shí)間可能會(huì)回退到過去的時(shí)間點(diǎn)。這會(huì)導(dǎo)致雪花算法生成的ID出現(xiàn)重復(fù),因?yàn)闀r(shí)間戳部分會(huì)重復(fù)。
解決時(shí)鐘回?fù)軉栴}的方法
等待機(jī)制:
- 當(dāng)檢測(cè)到時(shí)鐘回?fù)軙r(shí),生成器可以等待時(shí)間追上上次生成ID的時(shí)間戳,然后再生成新的ID。這種方法簡(jiǎn)單直接,但可能會(huì)導(dǎo)致生成器在等待期間無法生成新的ID。
擴(kuò)展位:
- 在ID結(jié)構(gòu)中增加額外的位來處理時(shí)鐘回?fù)?。例如,可以使用額外的位來記錄時(shí)鐘回?fù)艿拇螖?shù),從而避免ID重復(fù)。
預(yù)留時(shí)間戳:
- 在生成ID時(shí),預(yù)留一些時(shí)間戳范圍,用于處理時(shí)鐘回?fù)?。例如,可以預(yù)留一些時(shí)間戳范圍,當(dāng)檢測(cè)到時(shí)鐘回?fù)軙r(shí),使用預(yù)留的時(shí)間戳生成新的ID。
邏輯時(shí)鐘:
- 使用邏輯時(shí)鐘(如Lamport時(shí)鐘或Vector時(shí)鐘)代替物理時(shí)鐘。邏輯時(shí)鐘可以保證在分布式系統(tǒng)中事件的順序,避免時(shí)鐘回?fù)軉栴}。
示例代碼
以下是一個(gè)使用等待機(jī)制解決時(shí)鐘回?fù)軉栴}的雪花算法實(shí)現(xiàn)示例:
public class SnowflakeIdGenerator {
private final long twepoch = 1288834974657L; // 起始時(shí)間戳,例如Twitter的Snowflake起始時(shí)間
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) {
// 時(shí)鐘回?fù)?,等待時(shí)間追上
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());
}
}
代碼解釋
檢測(cè)時(shí)鐘回?fù)?/strong>:
- 在生成ID時(shí),首先獲取當(dāng)前時(shí)間戳,并與上次生成ID的時(shí)間戳進(jìn)行比較。
- 如果當(dāng)前時(shí)間戳小于上次生成ID的時(shí)間戳,說明發(fā)生了時(shí)鐘回?fù)堋?/li>
等待機(jī)制:
- 如果時(shí)鐘回?fù)艿臅r(shí)間差小于等于5毫秒,生成器會(huì)等待時(shí)間追上上次生成ID的時(shí)間戳。
- 如果時(shí)鐘回?fù)艿臅r(shí)間差大于5毫秒,拋出異常,拒絕生成ID。
生成新的ID:
- 如果時(shí)鐘沒有回?fù)?,或者等待時(shí)間追上后,生成新的ID。
總結(jié)
時(shí)鐘回?fù)軉栴}是分布式系統(tǒng)中使用雪花算法生成唯一ID時(shí)需要解決的一個(gè)重要問題。通過使用等待機(jī)制、擴(kuò)展位、預(yù)留時(shí)間戳或邏輯時(shí)鐘等方法,可以有效避免時(shí)鐘回?fù)軐?dǎo)致的ID重復(fù)問題。在實(shí)際應(yīng)用中,可以根據(jù)具體需求選擇合適的解決方案。
到此這篇關(guān)于java中雪花算法時(shí)鐘回?fù)軉栴}解決的文章就介紹到這了,更多相關(guān)java 雪花算法時(shí)鐘回?fù)軆?nèi)容請(qǐng)搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!
相關(guān)文章
springboot連接多個(gè)數(shù)據(jù)庫(kù)的實(shí)現(xiàn)方法
有時(shí)候一個(gè)SpringBoot項(xiàng)目需要同時(shí)連接兩個(gè)數(shù)據(jù)庫(kù),本文就來介紹一下springboot連接多個(gè)數(shù)據(jù)庫(kù)的實(shí)現(xiàn)方法,具有一定的參考價(jià)值,感興趣的可以了解一下2024-08-08
Springboot?異步任務(wù)和定時(shí)任務(wù)的異步處理
本文介紹了Springboot異步任務(wù)和定時(shí)任務(wù)的異步處理,Springboot?中,異步任務(wù)和定時(shí)任務(wù)是經(jīng)常遇到的處理問題方式,為了能夠用好這兩項(xiàng)配置,不干擾正常的業(yè)務(wù),需要對(duì)其進(jìn)行異步化配置。怎么設(shè)置合理的異步處理線程就是其核心和關(guān)鍵,下文詳情需要的朋友可以參考下2022-05-05
springboot整合nacos,如何讀取nacos配置文件
這篇文章主要介紹了springboot整合nacos,如何讀取nacos配置文件問題,具有很好的參考價(jià)值,希望對(duì)大家有所幫助。如有錯(cuò)誤或未考慮完全的地方,望不吝賜教2022-11-11
java Timer 定時(shí)每天凌晨1點(diǎn)執(zhí)行任務(wù)
這篇文章主要介紹了java Timer 定時(shí)每天凌晨1點(diǎn)執(zhí)行任務(wù)的代碼,代碼簡(jiǎn)單易懂,非常不錯(cuò),具有一定的參考借鑒價(jià)值,需要的朋友可以參考下2019-09-09
解決IntellIJ IDEA提示內(nèi)存不足的圖文教程
現(xiàn)在越來越多的人投入了 IntellIJ Idea 的懷抱, 它給我們的日常開發(fā)帶來了諸多便利,但是我們?cè)陂_發(fā)過程中,總是能碰到idea內(nèi)存不足問題,所以本文給大家介紹了解決IntellIJ IDEA提示內(nèi)存不足的圖文教程,需要的朋友可以參考下2025-03-03
SpringBoot集成Druid連接池連接MySQL8.0.11
這篇博客簡(jiǎn)單介紹spring boot集成druid連接池的簡(jiǎn)單配置和注意事項(xiàng),文中通過示例代碼介紹的非常詳細(xì),需要的朋友們下面隨著小編來一起學(xué)習(xí)學(xué)習(xí)吧2021-07-07
Java實(shí)戰(zhàn)之基于I/O流設(shè)計(jì)的圖書管理系統(tǒng)
這篇文章主要介紹了Java實(shí)戰(zhàn)之基于I/O流設(shè)計(jì)的圖書館管理系統(tǒng),文中有非常詳細(xì)的代碼示例,對(duì)正在學(xué)習(xí)java的小伙伴們有非常好的幫助,需要的朋友可以參考下2021-04-04

