java中使用雪花算法(Snowflake)為分布式系統(tǒng)生成全局唯一ID代碼示例
(全局唯一ID的解決方案有很多種,這里主要是介紹和學(xué)習(xí)Snowflake算法)
什么是雪花算法(Snowflake)
雪花算法(Snowflake Algorithm)是由Twitter公司在2010年左右提出的一種分布式ID生成算法,主要用于生成全局唯一且趨勢遞增的ID。這種算法生成的ID是一個64位的長整型數(shù)字,具有很高的性能與擴展性,特別適合于分布式環(huán)境下的主鍵生成場景,比如數(shù)據(jù)庫表主鍵、消息隊列的Message ID等。
實現(xiàn)原理
Snowflake算法的原理主要體現(xiàn)在它生成64位ID的結(jié)構(gòu)上,主要劃分為如下幾個部分:
0 | 00000000000000000000000000000000000000000 | 00000 | 00000 | 000000000000
- 1bit-符號位:
第1位通常固定為0,表示生成的ID都是正數(shù)。
- 41bit-時間戳部分:
從第2位到第42位(共41位)存儲時間戳信息,精確到毫秒級別。時間戳可以是自定義的一個起始時間點(如Twitter使用的是2010-11-04的某一時刻),這樣可以通過比較ID中的時間戳部分來判斷事件發(fā)生的先后順序。41位的時間截,可以使用69年,年T = (1L << 41) / (1000L * 60 * 60 * 24 * 365) = 69。
- 10bit-工作機器ID(5bit數(shù)據(jù)中心ID+5bit機器ID):
從第43位到第52位(共10位)存儲工作機器ID或者數(shù)據(jù)中心ID。這部分可以進(jìn)一步細(xì)分為兩部分,例如前5位標(biāo)識數(shù)據(jù)中心ID,后5位標(biāo)識工作節(jié)點ID。這樣可以支持32(0~31)個數(shù)據(jù)中心以及每個數(shù)據(jù)中心內(nèi)部的32(0~31)個工作節(jié)點,足夠覆蓋大規(guī)模分布式系統(tǒng)的節(jié)點標(biāo)識。
- 12bit-序列號部分:
從第53位到第64位(共12位)存儲同一節(jié)點同一毫秒內(nèi)生成的序列號,這意味著同一個節(jié)點在同毫秒內(nèi)可以生成最多4096個不同的ID(2^12)。
當(dāng)生成ID時,首先獲取當(dāng)前時間戳,然后加上工作節(jié)點ID以及序列號。如果在同一毫秒內(nèi)有新的請求,則序列號加1。若序列號達(dá)到最大值,則等待下一毫秒再進(jìn)行分配,從而確保在同一節(jié)點內(nèi)生成的ID是唯一的
雪花算法的優(yōu)缺點
優(yōu)點:
全局唯一性:雪花算法生成的ID是全局唯一的,這在分布式系統(tǒng)中非常重要,可以避免因ID沖突而導(dǎo)致的數(shù)據(jù)不一致問題。
遞增有序:由于ID中包含時間戳部分,所以生成的ID是遞增有序的。這有助于數(shù)據(jù)庫插入性能的優(yōu)化,因為有序的ID可以減少數(shù)據(jù)庫的頁分裂,提高寫入效率。
靈活性:雪花算法允許自定義配置工作機器ID和數(shù)據(jù)中心ID的位數(shù),可以根據(jù)實際部署環(huán)境調(diào)整這些配置,以支持不同規(guī)模的分布式系統(tǒng)。
高效性:算法本身實現(xiàn)簡單,生成ID的速度快,能夠滿足高并發(fā)場景下的需求。
缺點:
時鐘依賴:雪花算法依賴于系統(tǒng)時鐘來生成時間戳部分。如果系統(tǒng)時鐘出現(xiàn)回?fù)芑蚱?,可能會?dǎo)致生成的ID不唯一或有序性受到破壞。雖然可以通過一些機制來處理時鐘回?fù)軉栴},但時鐘漂移仍然是一個潛在的風(fēng)險。
機器ID沖突:如果部署的工作節(jié)點數(shù)量超過了算法中定義的機器ID位數(shù)所能表示的范圍,就會發(fā)生機器ID沖突。這需要在設(shè)計系統(tǒng)時預(yù)先規(guī)劃好機器ID的分配和管理。
缺乏安全性:雪花算法生成的ID本身并不包含加密或簽名信息,因此容易受到惡意篡改。如果ID的安全性要求較高,需要在生成ID后添加額外的加密或簽名措施。
擴展性限制:由于雪花算法的ID結(jié)構(gòu)是固定的,因此在某些情況下可能會受到擴展性的限制。例如,如果未來需要添加更多的元數(shù)據(jù)到ID中,或者需要支持更大的分布式系統(tǒng)規(guī)模,可能需要重新設(shè)計ID生成算法。
因此,為了更全面地解決雪花算法的缺陷問題,可能需要采取額外的措施,例如:
增強時鐘同步:使用NTP(Network Time Protocol)或其他時鐘同步機制來確保各個節(jié)點之間的時鐘盡可能準(zhǔn)確同步。
增加機器ID的靈活性:設(shè)計一種更靈活的方式來分配和管理機器ID,以便支持更多的工作節(jié)點和數(shù)據(jù)中心。
安全性考慮:對生成的ID進(jìn)行加密或簽名,以防止惡意篡改。
綜上所述,雪花算法在分布式系統(tǒng)中具有廣泛的應(yīng)用價值,其全局唯一性和遞增有序性使得它成為生成唯一ID的優(yōu)選方案之一。然而,在使用雪花算法時也需要注意其潛在的缺點,并根據(jù)實際需求進(jìn)行配置和優(yōu)化。
Snowflake算法生成ID的Java代碼示例
以下是Snowflake算法的一個java簡化版實現(xiàn):
public class SnowflakeIdWorker { // 起始的時間戳(自定義,例如系統(tǒng)上線時間) private final long twepoch = 1288834974657L; // 機器id所占的位數(shù) private final long workerIdBits = 5L; // 數(shù)據(jù)標(biāo)識id所占的位數(shù) private final long datacenterIdBits = 5L; // 最大機器ID private final long maxWorkerId = -1L ^ (-1L << workerIdBits); // 最大數(shù)據(jù)標(biāo)識ID private final long maxDatacenterId = -1L ^ (-1L << datacenterIdBits); // 序列在id中占的位數(shù) private final long sequenceBits = 12L; // 機器ID左移12位 private final long workerIdShift = sequenceBits; // 數(shù)據(jù)標(biāo)識id左移17位(12+5) private final long datacenterIdShift = sequenceBits + workerIdBits; // 時間截左移22位(5+5+12) private final long timestampLeftShift = sequenceBits + workerIdBits + datacenterIdBits; // 序列的掩碼,這里為4095 (0b111111111111=4095) private final long sequenceMask = -1L ^ (-1L << sequenceBits); // 上次生成ID的時間截 private long lastTimestamp = -1L; // 序列號 private long sequence = 0L; // 工作機器ID private final long workerId; // 數(shù)據(jù)中心ID private final long datacenterId; public SnowflakeIdWorker(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; } // 生成ID public synchronized long nextId() { long timestamp = timeGen(); // 如果當(dāng)前時間小于上一次ID生成的時間戳,說明系統(tǒng)時鐘回退,拋出異常 if (timestamp < lastTimestamp) { throw new RuntimeException(String.format("Clock moved backwards. Refusing to generate id for %d milliseconds", lastTimestamp - timestamp)); } // 如果時間戳相同,則序列號自增 if (lastTimestamp == timestamp) { sequence = (sequence + 1) & sequenceMask; // 序列號溢出,等待下一毫秒 if (sequence == 0) { timestamp = tilNextMillis(lastTimestamp); } } else { // 時間戳改變,序列號重置為0 sequence = 0L; } // 更新最后的時間戳 lastTimestamp = timestamp; // 移位并通過或運算拼到一起組成64位的ID return ((timestamp - twepoch) << timestampLeftShift) | (datacenterId << datacenterIdShift) | (workerId << workerIdShift) | sequence; } // 獲取當(dāng)前時間戳 protected long timeGen() { return System.currentTimeMillis(); } // 等待下一個毫秒 protected long tilNextMillis(long lastTimestamp) { long timestamp = timeGen(); while (timestamp <= lastTimestamp) { timestamp = timeGen(); } return timestamp; } public static void main(String[] args) { SnowflakeIdWorker idWorker = new SnowflakeIdWorker(1, 1); for (int i = 0; i < 5; i++) { long id = idWorker.nextId(); System.out.println(Long.toBinaryString(id)); System.out.println(id); } } }
代碼輸出:
這段代碼實現(xiàn)了雪花算法的核心邏輯。在nextId()
方法中,它首先獲取當(dāng)前時間戳,然后檢查時間戳是否小于上一次生成ID時的時間戳,如果是,則拋出異常,因為這意味著系統(tǒng)時鐘回退,可能會導(dǎo)致ID生成出現(xiàn)混亂。如果時間戳相同,則序列號自增,并檢查是否溢出,如果溢出則等待下一個毫秒。如果時間戳不同,則重置序列號。最后,將時間戳、數(shù)據(jù)中心ID、機器ID和序列號按照各自的偏移量左移,然后進(jìn)行位或運算,組合成一個64位的ID。
(注:關(guān)于數(shù)據(jù)中心ID、機器ID,根據(jù)實際情況來進(jìn)行配置。)
總結(jié)
到此這篇關(guān)于java中使用雪花算法(Snowflake)為分布式系統(tǒng)生成全局唯一ID的文章就介紹到這了,更多相關(guān)java雪花算法生成全局唯一ID內(nèi)容請搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!
相關(guān)文章
Java?Map.values()方法之如何獲取Map集合中的所有鍵值對象
這篇文章主要介紹了Java?Map.values()方法之如何獲取Map集合中的所有鍵值對象問題,具有很好的參考價值,希望對大家有所幫助。如有錯誤或未考慮完全的地方,望不吝賜教2023-03-03ShardingSphere jdbc實現(xiàn)分庫分表核心概念詳解
這篇文章主要為大家介紹了ShardingSphere jdbc實現(xiàn)分庫分表核心概念詳解,有需要的朋友可以借鑒參考下,希望能夠有所幫助,祝大家多多進(jìn)步,早日升職加薪2022-12-12JSON反序列化Long變Integer或Double的問題及解決
這篇文章主要介紹了JSON反序列化Long變Integer或Double的問題及解決方案,具有很好的參考價值,希望對大家有所幫助。如有錯誤或未考慮完全的地方,望不吝賜教2022-01-01IDEA POM文件配置profile實現(xiàn)不同環(huán)境切換的方法步驟
這篇文章主要介紹了IDEA POM文件配置profile實現(xiàn)不同環(huán)境切換的方法步驟2024-03-03在SpringBoot中使用MongoDB完成數(shù)據(jù)存儲
本文主要介紹了在SpringBoot中如惡化使用MongoDB完成數(shù)據(jù)存儲,接下來這篇我們將圍繞MongoDB進(jìn)行,MongoDB是一個開源的,面向文檔的NoSQL數(shù)據(jù)庫管理系統(tǒng),使用類似JSON的BSON(二進(jìn)制JSON)格式來存儲數(shù)據(jù),具有靈活的數(shù)據(jù)模型和強大的查詢功能,需要的朋友可以參考下2023-11-11