柚子快報(bào)邀請(qǐng)碼778899分享:開(kāi)發(fā)語(yǔ)言 Java分布式ID
柚子快報(bào)邀請(qǐng)碼778899分享:開(kāi)發(fā)語(yǔ)言 Java分布式ID
1?什么是分布式ID
????????分布式ID是指在分布式系統(tǒng)中生成的唯一標(biāo)識(shí)符,用于標(biāo)識(shí)不同實(shí)體或數(shù)據(jù)的唯一性。在分布式系統(tǒng)中,多臺(tái)機(jī)器并行處理任務(wù),為了確保生成的ID在整個(gè)系統(tǒng)中的唯一性,需要采用特殊的算法來(lái)生成分布式ID。
????????在傳統(tǒng)的單機(jī)系統(tǒng)中,可以使用自增序列或隨機(jī)數(shù)來(lái)生成唯一ID。但在分布式系統(tǒng)中,多臺(tái)機(jī)器同時(shí)生成ID時(shí)可能會(huì)導(dǎo)致重復(fù)的情況。為了解決這個(gè)問(wèn)題,需要引入一種分布式ID生成算法,確保在整個(gè)分布式系統(tǒng)中生成的ID是唯一的。
????????分布式ID的設(shè)計(jì)要考慮并發(fā)性能、全局唯一性和擴(kuò)展性等因素,并根據(jù)具體的系統(tǒng)需求選擇合適的算法實(shí)現(xiàn)。常見(jiàn)的分布式ID生成算法包括雪花算法(Snowflake)、UUID(Universally Unique Identifier)等。
2 UUID
????????UUID(Universally Unique Identifier)是一種標(biāo)識(shí)符,用于在計(jì)算系統(tǒng)中生成全局唯一的ID。它是由128位的二進(jìn)制數(shù)表示,通常以32位的十六進(jìn)制字符串形式展示。
????????UUID的生成算法保證了在全球范圍內(nèi)生成的ID具有極高的唯一性。它不依賴于中央控制器或集中式的ID生成服務(wù),可以在分布式系統(tǒng)中生成唯一的標(biāo)識(shí)符。
????????生成的UUID通常呈現(xiàn)為以下形式:
????????xxxxxxxx-xxxx-Mxxx-Nxxx-xxxxxxxxxxxx
????????其中,"x"表示十六進(jìn)制數(shù)字。具體的格式和含義取決于UUID的版本和變體。
????????UUID具有以下特點(diǎn):
全局唯一性:UUID的生成算法確保在全球范圍內(nèi)生成的ID具有極高的唯一性,不同系統(tǒng)和節(jié)點(diǎn)生成的UUID幾乎不會(huì)重復(fù)。無(wú)序性:UUID是基于隨機(jī)數(shù)或名稱空間生成的,沒(méi)有嚴(yán)格的遞增或遞減順序??捎眯裕篣UID生成算法簡(jiǎn)單高效,生成過(guò)程不需要依賴網(wǎng)絡(luò)通信或中央服務(wù)器。
????????UUID廣泛應(yīng)用于分布式系統(tǒng)、數(shù)據(jù)庫(kù)、標(biāo)識(shí)符生成等場(chǎng)景,用于唯一標(biāo)識(shí)實(shí)體、數(shù)據(jù)記錄或資源。它在保證全局唯一性的同時(shí),提供了一種簡(jiǎn)單可靠的標(biāo)識(shí)方案。
????????在Java中,UUID類是用于生成和操作UUID的工具類,提供了方便的方法來(lái)生成和操作UUID。它可以用于在Java應(yīng)用程序中生成唯一的標(biāo)識(shí)符,例如在分布式系統(tǒng)中跟蹤實(shí)體或記錄的唯一標(biāo)識(shí)。
????????UUID類位于java.util包下,并提供了以下主要方法:
randomUUID(): 靜態(tài)方法,用于生成一個(gè)隨機(jī)的UUID。該方法會(huì)使用隨機(jī)數(shù)生成算法生成一個(gè)符合UUID標(biāo)準(zhǔn)的隨機(jī)UUID。toString(): 將UUID對(duì)象轉(zhuǎn)換為字符串表示。
????????使用java生產(chǎn)一個(gè)UUID的代碼示意如下:
UUID uuid = UUID.randomUUID();
String uuidString = uuid.toString();
????????UUID的優(yōu)勢(shì)與劣勢(shì)
????????優(yōu)勢(shì):
全局唯一性:UUID的生成算法保證了生成的ID在全球范圍內(nèi)的唯一性,不依賴于中央控制器或集中式的ID生成服務(wù)。這意味著在分布式系統(tǒng)中不同節(jié)點(diǎn)生成的UUID不會(huì)發(fā)生沖突。無(wú)序性:UUID是基于隨機(jī)數(shù)生成的,沒(méi)有嚴(yán)格的遞增或遞減的順序。這對(duì)于某些應(yīng)用場(chǎng)景來(lái)說(shuō)是有利的,因?yàn)樗粫?huì)暴露數(shù)據(jù)生成的時(shí)間或其他敏感信息。簡(jiǎn)單易用:UUID的生成算法相對(duì)簡(jiǎn)單,生成過(guò)程高效,可以快速地生成ID。無(wú)需網(wǎng)絡(luò)通信:生成UUID不需要依賴網(wǎng)絡(luò)通信或中央服務(wù)器,每個(gè)節(jié)點(diǎn)可以獨(dú)立生成ID,減少了系統(tǒng)的復(fù)雜性和對(duì)網(wǎng)絡(luò)的依賴。
????????劣勢(shì):
長(zhǎng)度較長(zhǎng):UUID通常由32位的十六進(jìn)制數(shù)表示,加上分隔符的話長(zhǎng)度更長(zhǎng)。這會(huì)增加存儲(chǔ)和傳輸?shù)某杀?,特別是在大規(guī)模的數(shù)據(jù)集合中使用UUID作為標(biāo)識(shí)符時(shí)。不可讀性:UUID是由數(shù)字和字母組成的字符串,對(duì)人類來(lái)說(shuō)不太友好,不易于直觀理解。這在調(diào)試、日志記錄和數(shù)據(jù)查詢等場(chǎng)景中可能會(huì)帶來(lái)一些困難。無(wú)法排序:由于UUID是基于隨機(jī)數(shù)生成的,它們之間沒(méi)有嚴(yán)格的順序關(guān)系。這導(dǎo)致在某些需要按照時(shí)間或順序訪問(wèn)數(shù)據(jù)的場(chǎng)景中,UUID并不適合作為排序依據(jù)。不適合作為數(shù)據(jù)庫(kù)索引:由于UUID的無(wú)序性和長(zhǎng)度較長(zhǎng),將UUID作為數(shù)據(jù)庫(kù)的主鍵或索引可能會(huì)導(dǎo)致性能下降,尤其是在大規(guī)模數(shù)據(jù)集合和頻繁的索引操作中。
3?雪花算法
????????雪花算法(Snowflake Algorithm)是一種常用的分布式ID生成算法,最初由Twitter開(kāi)發(fā)并廣泛應(yīng)用于分布式系統(tǒng)中。它的設(shè)計(jì)目標(biāo)是生成全局唯一且有序遞增的ID,適用于大規(guī)模分布式系統(tǒng)中的標(biāo)識(shí)符需求。相對(duì)于UUID,雪花算法ID的好處是長(zhǎng)度短并且還是有序遞增的。
????????雪花算法生成的ID是一個(gè)64位的長(zhǎng)整型數(shù)值,具體格式如下所示:
0 | 0000000000 | 00000 | 00000 | 000000000000
????????其中:最高位是未使用的符號(hào)位(為0),接下來(lái)的41位表示時(shí)間戳,然后是5位的數(shù)據(jù)中心標(biāo)識(shí)符,5位的機(jī)器標(biāo)識(shí)符,最后是12位的序列號(hào)。
????????雪花算法生成ID的過(guò)程如下:
時(shí)間戳:使用當(dāng)前時(shí)間戳減去一個(gè)固定的起始時(shí)間(如2010年1月1日),得到一個(gè)相對(duì)時(shí)間。這樣可以確保在一定時(shí)間內(nèi)生成的ID具有遞增的趨勢(shì)。數(shù)據(jù)中心標(biāo)識(shí)符和機(jī)器標(biāo)識(shí)符:每個(gè)數(shù)據(jù)中心分配一個(gè)唯一的數(shù)據(jù)中心標(biāo)識(shí)符,每臺(tái)機(jī)器分配一個(gè)唯一的機(jī)器標(biāo)識(shí)符。這樣可以在分布式環(huán)境中唯一標(biāo)識(shí)每個(gè)數(shù)據(jù)中心和每臺(tái)機(jī)器。序列號(hào):在同一毫秒內(nèi)生成的ID,通過(guò)序列號(hào)來(lái)進(jìn)行區(qū)分,保證同一機(jī)器在同一毫秒內(nèi)生成的ID的唯一性。序列號(hào)從0開(kāi)始,每生成一個(gè)ID自增1,最多可以達(dá)到12位的長(zhǎng)度(即4096個(gè)序列號(hào))。
????????雪花算法的優(yōu)點(diǎn)包括:
全局唯一性:在分布式系統(tǒng)中,不同節(jié)點(diǎn)生成的ID不會(huì)產(chǎn)生沖突,確保全局唯一性。有序性:生成的ID在時(shí)間上有序遞增,方便按照時(shí)間排序和索引。高性能:生成ID的算法簡(jiǎn)單高效,不依賴于網(wǎng)絡(luò)通信或中央服務(wù)器。
????????然而,雪花算法也有一些限制:
依賴于系統(tǒng)時(shí)間:由于使用時(shí)間戳作為ID的一部分,系統(tǒng)時(shí)間的回?fù)芑虿煌?jié)點(diǎn)之間的時(shí)間差異可能會(huì)導(dǎo)致生成的ID不唯一或不按照預(yù)期順序遞增。數(shù)據(jù)中心和機(jī)器標(biāo)識(shí)符的分配需要管理:為每個(gè)數(shù)據(jù)中心和機(jī)器分配唯一標(biāo)識(shí)符需要一定的管理工作,確保標(biāo)識(shí)符的唯一性。時(shí)鐘回?fù)軉?wèn)題:如果系統(tǒng)時(shí)間發(fā)生回?fù)埽ㄐ?zhǔn)調(diào)整時(shí)間),可能會(huì)導(dǎo)致生成的ID不唯一或不按照預(yù)期遞增。
????????在使用雪花算法時(shí),需要根據(jù)具體應(yīng)用的需求和場(chǎng)景,合理設(shè)置數(shù)據(jù)。
/**
* 雪花算法
*/
public class SnowFlake {
/** 開(kāi)始時(shí)間截 (2020-01-01) */
private final long twepoch = 1577808000000L;
/** 機(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 = 12L;
/** 機(jī)器ID向左移12位 */
private final long workerIdShift = sequenceBits;
/** 數(shù)據(jù)標(biāo)識(shí)id向左移17位(12+5) */
private final long dataCenterIdShift = sequenceBits + workerIdBits;
/** 時(shí)間截向左移22位(5+5+12) */
private final long timestampLeftShift = sequenceBits + workerIdBits + dataCenterIdBits;
/** 生成序列的掩碼,這里為4095 (0b111111111111=0xfff=4095) */
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~4095) */
private long sequence = 0L;
/** 上次生成ID的時(shí)間截 */
private long lastTimestamp = -1L;
private static SnowFlake idWorker;
static {
idWorker = new SnowFlake(getWorkId(),getDataCenterId());
}
//==============================Constructors=====================================
/**
* 構(gòu)造函數(shù)
* @param workerId 工作ID (0~31)
* @param dataCenterId 數(shù)據(jù)中心ID (0~31)
*/
public SnowFlake(long workerId, long dataCenterId) {
if (workerId > maxWorkerId || workerId < 0) {
throw new IllegalArgumentException(String.format("workerId can't be greater than %d or less than 0", maxWorkerId));
}
if (dataCenterId > maxDataCenterId || dataCenterId < 0) {
throw new IllegalArgumentException(String.format("dataCenterId 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() {
long timestamp = timeGen();
//如果當(dāng)前時(shí)間小于上一次ID生成的時(shí)間戳,說(shuō)明系統(tǒng)時(shí)鐘回退過(guò)這個(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;
//移位并通過(guò)或運(yùn)算拼到一起組成64位的ID
return ((timestamp - twepoch) << timestampLeftShift)
| (dataCenterId << dataCenterIdShift)
| (workerId << workerIdShift)
| sequence;
}
/**
* 阻塞到下一個(gè)毫秒,直到獲得新的時(shí)間戳
* @param lastTimestamp 上次生成ID的時(shí)間截
* @return 當(dāng)前時(shí)間戳
*/
protected long tilNextMillis(long lastTimestamp) {
long timestamp = timeGen();
while (timestamp <= lastTimestamp) {
timestamp = timeGen();
}
return timestamp;
}
/**
* 返回以毫秒為單位的當(dāng)前時(shí)間
* @return 當(dāng)前時(shí)間(毫秒)
*/
protected long timeGen() {
return System.currentTimeMillis();
}
private static Long getWorkId(){
try {
String hostAddress = Inet4Address.getLocalHost().getHostAddress();
int[] ints = StringUtils.toCodePoints(hostAddress);
int sums = 0;
for(int b : ints){
sums += b;
}
return (long)(sums % 32);
} catch (UnknownHostException e) {
// 如果獲取失敗,則使用隨機(jī)數(shù)備用
return RandomUtils.nextLong(0,31);
}
}
private static Long getDataCenterId(){
int[] ints = StringUtils.toCodePoints(StringUtils.isEmpty(SystemUtils.getHostName())?"defaultvalue":SystemUtils.getHostName());
int sums = 0;
for (int i: ints) {
sums += i;
}
return (long)(sums % 32);
}
/**
* 靜態(tài)工具類
*
* @return
*/
public static Long generateId(){
long id = idWorker.nextId();
return id;
}
//==================Test===================
/** 測(cè)試 */
public static void main(String[] args) {
System.out.println(System.currentTimeMillis());
long startTime = System.nanoTime();
for (int i = 0; i < 50000; i++) {
long id = SnowFlake.generateId();
System.out.println(id);
}
System.out.println((System.nanoTime()-startTime)/1000000+"ms");
}
}
????????使用上述的雪花算法實(shí)現(xiàn),調(diào)用 SnowFlake.generateId() 方法來(lái)生成唯一的ID。確保為每個(gè)實(shí)例分配唯一的數(shù)據(jù)中心ID和機(jī)器ID。
????????這樣,你就能夠在童小碼項(xiàng)目中生成分布式有序的商品、課程等業(yè)務(wù)關(guān)鍵字的ID,同時(shí)保持高性能和全局唯一性。
4 總結(jié)
????????1、分布式ID是指在分布式系統(tǒng)中生成的唯一標(biāo)識(shí)符,用于標(biāo)識(shí)不同實(shí)體或數(shù)據(jù)的唯一性
在分布式系統(tǒng)中,多臺(tái)機(jī)器并行處理任務(wù),為了確保生成的ID在整個(gè)系統(tǒng)中的唯一性,需要采用特殊的算法來(lái)生成分布式ID常用方式有雪花算法、UUID等
????????2、UUID(Universally Unique Identifier)是一種標(biāo)識(shí)符,用于在計(jì)算系統(tǒng)中生成全局唯一的ID
由128位的二進(jìn)制數(shù)表示,通常以32位的十六進(jìn)制字符串形式展示長(zhǎng)度較長(zhǎng)、不可讀性、無(wú)法排序和不適合作為數(shù)據(jù)庫(kù)索引等劣勢(shì)需要在具體應(yīng)用中進(jìn)行權(quán)衡和考慮
????????3、雪花算法(Snowflake Algorithm)是一種常用的分布式ID生成算法
生成全局唯一且有序遞增的ID,適用于大規(guī)模分布式系統(tǒng)中的標(biāo)識(shí)符需求相對(duì)于UUID,雪花算法ID的好處是長(zhǎng)度短并且還是有序遞增的
柚子快報(bào)邀請(qǐng)碼778899分享:開(kāi)發(fā)語(yǔ)言 Java分布式ID
精彩內(nèi)容
本文內(nèi)容根據(jù)網(wǎng)絡(luò)資料整理,出于傳遞更多信息之目的,不代表金鑰匙跨境贊同其觀點(diǎn)和立場(chǎng)。
轉(zhuǎn)載請(qǐng)注明,如有侵權(quán),聯(lián)系刪除。