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