當(dāng)前位置:
首頁 >
java生成唯一有序序列号_分布式唯一 ID 之 Snowflake 算法
發(fā)布時(shí)間:2023/12/3
44
豆豆
生活随笔
收集整理的這篇文章主要介紹了
java生成唯一有序序列号_分布式唯一 ID 之 Snowflake 算法
小編覺得挺不錯(cuò)的,現(xiàn)在分享給大家,幫大家做個(gè)參考.
SegmentFault 社區(qū)專欄:全棧修仙之路作者:semlinker
?*?twitter的snowflake算法?--?java實(shí)現(xiàn)
?*?
?*?@author?beyond
?*?@date?2016/11/26
?*/public?class?SnowFlake?{/**
?????*?起始的時(shí)間戳
?????*/private?final?static?long?START_STMP?=?1480166465631L;/**
?????*?每一部分占用的位數(shù)
?????*/private?final?static?long?SEQUENCE_BIT?=?12;?//序列號占用的位數(shù)private?final?static?long?MACHINE_BIT?=?5;???//機(jī)器標(biāo)識占用的位數(shù)private?final?static?long?DATACENTER_BIT?=?5;//數(shù)據(jù)中心占用的位數(shù)/**
?????*?每一部分的最大值
?????*/private?final?static?long?MAX_DATACENTER_NUM?=?-1L?^?(-1L?<????private?final?static?long?MAX_MACHINE_NUM?=?-1L?^?(-1L?<????private?final?static?long?MAX_SEQUENCE?=?-1L?^?(-1L?</**
?????*?每一部分向左的位移
?????*/private?final?static?long?MACHINE_LEFT?=?SEQUENCE_BIT;private?final?static?long?DATACENTER_LEFT?=?SEQUENCE_BIT?+?MACHINE_BIT;private?final?static?long?TIMESTMP_LEFT?=?DATACENTER_LEFT?+?DATACENTER_BIT;private?long?datacenterId;??//數(shù)據(jù)中心private?long?machineId;?????//機(jī)器標(biāo)識private?long?sequence?=?0L;?//序列號private?long?lastStmp?=?-1L;//上一次時(shí)間戳public?SnowFlake(long?datacenterId,?long?machineId)?{if?(datacenterId?>?MAX_DATACENTER_NUM?||?datacenterId?0)?{throw?new?IllegalArgumentException("datacenterId?can't?be?greater?than?MAX_DATACENTER_NUM?or?less?than?0");
????????}if?(machineId?>?MAX_MACHINE_NUM?||?machineId?0)?{throw?new?IllegalArgumentException("machineId?can't?be?greater?than?MAX_MACHINE_NUM?or?less?than?0");
????????}this.datacenterId?=?datacenterId;this.machineId?=?machineId;
????}/**
?????*?產(chǎn)生下一個(gè)ID
?????*
?????*?@return
?????*/public?synchronized?long?nextId()?{long?currStmp?=?getNewstmp();if?(currStmp?????????????throw?new?RuntimeException("Clock?moved?backwards.??Refusing?to?generate?id");
????????}if?(currStmp?==?lastStmp)?{//相同毫秒內(nèi),序列號自增
????????????sequence?=?(sequence?+?1)?&?MAX_SEQUENCE;//同一毫秒的序列數(shù)已經(jīng)達(dá)到最大if?(sequence?==?0L)?{
????????????????currStmp?=?getNextMill();
????????????}
????????}?else?{//不同毫秒內(nèi),序列號置為0
????????????sequence?=?0L;
????????}
????????lastStmp?=?currStmp;return?(currStmp?-?START_STMP)?<//時(shí)間戳部分
????????????????|?datacenterId?<//數(shù)據(jù)中心部分
????????????????|?machineId?<//機(jī)器標(biāo)識部分
????????????????|?sequence;?????????????????????????????//序列號部分
????}private?long?getNextMill()?{long?mill?=?getNewstmp();while?(mill?<=?lastStmp)?{
????????????mill?=?getNewstmp();
????????}return?mill;
????}private?long?getNewstmp()?{return?System.currentTimeMillis();
????}public?static?void?main(String[]?args)?{
????????SnowFlake?snowFlake?=?new?SnowFlake(2,?3);for?(int?i?=?0;?i?1?<12);?i++)?{
????????????System.out.println(snowFlake.nextId());
????????}
????}
}在詳細(xì)分析之前,我們先來回顧一下 Snowflake 算法的 ID 構(gòu)成圖:
?*?起始的時(shí)間戳:Sat Nov 26 2016 21:21:05 GMT+0800?(中國標(biāo)準(zhǔn)時(shí)間)
?*/private?final?static?long?START_STMP?=?1480166465631L;接著繼續(xù)定義三個(gè) long 類型的靜態(tài)變量,來表示序列號和工作機(jī)器 ID 的占用位數(shù):/**
?*?每一部分占用的位數(shù)
?*/private?final?static?long?SEQUENCE_BIT?=?12;?//序列號占用的位數(shù)private?final?static?long?MACHINE_BIT?=?5;???//機(jī)器標(biāo)識占用的位數(shù)private?final?static?long?DATACENTER_BIT?=?5;//數(shù)據(jù)中心占用的位數(shù)此外還定義了每一部分的最大值,具體如下:/**
?*?每一部分的最大值
?*/private?final?static?long?MAX_DATACENTER_NUM?=?-1L?^?(-1L?<//?31private?final?static?long?MAX_MACHINE_NUM?=?-1L?^?(-1L?<//?31private?final?static?long?MAX_SEQUENCE?=?-1L?^?(-1L?<//?4095
????????}if?(machineId?>?MAX_MACHINE_NUM?||?machineId?0)?{throw?new?IllegalArgumentException("machineId?can't?be?greater?than?MAX_MACHINE_NUM?or?less?than?0");
????????}this.datacenterId?=?datacenterId;this.machineId?=?machineId;
}
?*?產(chǎn)生下一個(gè)ID
?*
?*?@return
?*/public?synchronized?long?nextId()?{//?獲取當(dāng)前的毫秒數(shù):System.currentTimeMillis(),該方法產(chǎn)生一個(gè)當(dāng)前的毫秒,這個(gè)毫秒//?其實(shí)就是自1970年1月1日0時(shí)起的毫秒數(shù)。long?currStmp?=?getNewstmp();//?private?long?lastTimeStamp?=?-1L;?表示上一次時(shí)間戳//?檢測是否出現(xiàn)時(shí)鐘回?fù)躨f?(currStmp??????throw?new?RuntimeException("Clock?moved?backwards.??Refusing?to?generate?id");
??}//?相同毫秒內(nèi),序列號自增if?(currStmp?==?lastStmp)?{//?private?long?sequence?=?0L;?序列號//?MAX_SEQUENCE?=??????4095?111111111111//?MAX_SEQUENCE?+?1?=?4096?1000000000000
?????sequence?=?(sequence?+?1)?&?MAX_SEQUENCE;//?同一毫秒的序列數(shù)已經(jīng)達(dá)到最大if?(sequence?==?0L)?{//?阻塞到下一個(gè)毫秒,獲得新的時(shí)間戳
???????????currStmp?=?getNextMill();
???????}
?????}?else?{//不同毫秒內(nèi),序列號置為0
????????????sequence?=?0L;
???}
???lastStmp?=?currStmp;//?MACHINE_LEFT?=?SEQUENCE_BIT;?->?12//?DATA_CENTER_LEFT?=?SEQUENCE_BIT?+?MACHINE_BIT;?->?17//?TIMESTAMP_LEFT?=?DATA_CENTER_LEFT?+?DATA_CENTER_BIT;?->?22return?(currStmp?-?START_STMP)?<//時(shí)間戳部分
????????????????|?datacenterId?<//數(shù)據(jù)中心部分
????????????????|?machineId?<//機(jī)器標(biāo)識部分
????????????????|?sequence;?????????????????????????????//序列號部分
}現(xiàn)在我們來看一下使用方式:public?static?void?main(String[]?args)?{
??SnowFlake?snowFlake?=?new?SnowFlake(2,?3);for?(int?i?=?0;?i?1?<12);?i++)?{
????System.out.println(snowFlake.nextId());
??}
}現(xiàn)在我們已經(jīng)可以利用 SnowFlake 對象生成唯一 ID 了,那這個(gè)唯一 ID 有什么用呢?這里舉一個(gè)簡單的應(yīng)用場景,即基于 SnowFlake 的短網(wǎng)址生成器,其主要思路是使用 SnowFlake 算法生成一個(gè)整數(shù),然后對該整數(shù)進(jìn)行 62 進(jìn)制編碼最終生成一個(gè)短地址 URL。對短網(wǎng)址生成器感興趣的小伙伴,可以參考徐劉根大佬在碼云上分享的工具類。最后我們來簡單總結(jié)一下,本文主要介紹了什么是 Snowflake(雪花)算法、Snowflake 算法 ID 構(gòu)成圖及其優(yōu)缺點(diǎn),最后詳細(xì)分析了 Github 上 beyondfengyu 大佬基于 Java 實(shí)現(xiàn)的 SnowFlake。在實(shí)際項(xiàng)目中,建議大家選用基于 Snowflake 算法成熟的開源項(xiàng)目,如百度的 UidGenerator 或美團(tuán)的 Leaf。
No.1
Snowflake 簡介
1.1 什么是 Snowflake
Snowflake is a service used to generate unique IDs for objects within Twitter?(Tweets, Direct Messages, Users, Collections, Lists etc.). These IDs are unique 64-bit unsigned integers, which are based>s is due to the way Javascript and other languages that consume JSON evaluate large integers. If you come across a scenario where it doesn’t appear that id and id_str match, it’s due to your environment having already parsed the id integer, munging the number in the process. —— developer.twitter.comSnowflake(雪花)是一項(xiàng)服務(wù),用于為 Twitter 內(nèi)的對象(推文,直接消息,用戶,集合,列表等)生成唯一的 ID。這些 IDs 是唯一的 64 位無符號整數(shù),它們基于時(shí)間,而不是順序的。完整的 ID 由時(shí)間戳,工作機(jī)器編號和序列號組成。當(dāng)在 API 中使用 JSON 數(shù)據(jù)格式時(shí),請務(wù)必始終使用 id_str 字段而不是 id,這一點(diǎn)很重要。這是由于處理 JSON 的 Javascript 和其他語言計(jì)算大整數(shù)的方式造成的。如果你遇到 id 和 id_str 似乎不匹配的情況,這是因?yàn)槟愕沫h(huán)境已經(jīng)解析了 id 整數(shù),并在處理的過程中仔細(xì)分析了這個(gè)數(shù)字。在 JavaScript 中,Number 基本類型可以精確表示的最大整數(shù)是 2^53。因此如果直接使用 Number 來表示 64 位的 Snowflake ID 肯定是行不通的。所以 Twitter 工程師們讓我們務(wù)必使用 id_str 字段即通過字符串來表示生成的 ID。當(dāng)然這個(gè)問題不僅僅存在于使用 Snowflake ID 的場景,為了解決 JavaScript 不能安全存儲和操作大整數(shù)的問題,BigInt 這個(gè)救星出現(xiàn)了,它是一種內(nèi)置對象,可以表示大于 2^53 的整數(shù),甚至是任意大的整數(shù)。BigInt 現(xiàn)在處在 ECMAScript 標(biāo)準(zhǔn)化過程中的第三階段。當(dāng)它進(jìn)入第四階段草案,也就是最終標(biāo)準(zhǔn)時(shí),BigInt 將成為 Javacript 中的第二種內(nèi)置數(shù)值類型。BigInt 可能會成為自 ES2015 引入 Symbol 之后,增加的第一個(gè)新的內(nèi)置類型。1.2 Snowflake 算法
下圖是 Snowflake 算法的 ID 構(gòu)成圖:???1 位標(biāo)識部分,該位不用主要是為了保持 ID 的自增特性,若使用了最高位,int64_t 會表示為負(fù)數(shù)。在 Java 中由于 long 類型的最高位是符號位,正數(shù)是 0,負(fù)數(shù)是 1,一般生成的 ID 為正整數(shù),所以最高位為 0;???41 位時(shí)間戳部分,這個(gè)是毫秒級的時(shí)間,一般實(shí)現(xiàn)上不會存儲當(dāng)前的時(shí)間戳,而是時(shí)間戳的差值(當(dāng)前時(shí)間減去固定的開始時(shí)間),這樣可以使產(chǎn)生的 ID 從更小值開始;41 位的時(shí)間戳可以使用 69 年,(1L << 41) / (1000L 60 60 24 365) = (2199023255552 / 31536000000) ≈ 69.73 年;???10 位工作機(jī)器 ID 部分,Twitter 實(shí)現(xiàn)中使用前 5 位作為數(shù)據(jù)中心標(biāo)識,后 5 位作為機(jī)器標(biāo)識,可以部署 1024 (2^10)個(gè)節(jié)點(diǎn);???12 位序列號部分,支持同一毫秒內(nèi)同一個(gè)節(jié)點(diǎn)可以生成 4096 (2^12)個(gè) ID;Snowflake 算法生成的 ID 大致上是按照時(shí)間遞增的,用在分布式系統(tǒng)中時(shí),需要注意數(shù)據(jù)中心標(biāo)識和機(jī)器標(biāo)識必須唯一,這樣就能保證每個(gè)節(jié)點(diǎn)生成的 ID 都是唯一的。我們不一定需要像 Twitter 那樣使用 5 位作為數(shù)據(jù)中心標(biāo)識,另 5 位作為機(jī)器標(biāo)識,可以根據(jù)我們業(yè)務(wù)的需要,靈活分配工作機(jī)器 ID 部分。比如:若不需要數(shù)據(jù)中心,完全可以使用全部 10 位作為機(jī)器標(biāo)識;若數(shù)據(jù)中心不多,也可以只使用 3 位作為數(shù)據(jù)中心,7 位作為機(jī)器標(biāo)識。No.2
Snowflake 解惑
以下問題來源于漫漫路博客 - “Twitter-Snowflake,64 位自增ID算法詳解” 評論區(qū)2.1 既然是 64 位,為何第一位不使用?
首位不用主要是為了保持 ID 的自增特性,若使用了最高位,int64_t 會表示為負(fù)數(shù)。在 Java 中由于 long 類型的最高位是符號位,正數(shù)是 0,負(fù)數(shù)是 1,一般生成的 ID 為正整數(shù),所以最高位為 0。2.2 怎么生成 41 位的時(shí)間戳?
41 位的時(shí)間戳,這個(gè)是毫秒級的時(shí)間,一般實(shí)現(xiàn)上不會存儲當(dāng)前的時(shí)間戳,而是時(shí)間戳的差值(當(dāng)前時(shí)間減去固定的開始時(shí)間)。41 位只是預(yù)留位(主要目的是約定使用年限,固定的開始時(shí)間),不用的位數(shù)填 0 就好了。2.3 工作機(jī)器 id 如果使用 MAC 地址的話,怎么轉(zhuǎn)成 10 bit?
網(wǎng)絡(luò)中每臺設(shè)備都有一個(gè)唯一的網(wǎng)絡(luò)標(biāo)識,這個(gè)地址叫 MAC 地址或網(wǎng)卡地址,由網(wǎng)絡(luò)設(shè)備制造商生產(chǎn)時(shí)寫在硬件內(nèi)部。MAC 地址則是 48 位的(6 個(gè)字節(jié)),通常表示為 12 個(gè) 16 進(jìn)制數(shù),每 2 個(gè) 16 進(jìn)制數(shù)之間用冒號隔開,如08:00:20:0A:8C:6D 就是一個(gè) MAC 地址。具體如下圖所示,其前 3 字節(jié)表示OUI(Organizationally Unique Identifier),是 IEEE (電氣和電子工程師協(xié)會)區(qū)分不同的廠家,后 3 字節(jié)由廠家自行分配。(圖片來源 - 百度百科)很明顯 Mac 地址是 48 位,而我們的工作機(jī)器 ID 部分只有 10 位,因此并不能直接使用 Mac 地址作為工作機(jī)器 ID。若要選用 Mac 地址的話,還需使用一個(gè)額外的工作機(jī)器 ID 分配器,用來實(shí)現(xiàn) ID 與 Mac 地址間的唯一映射。2.4 怎么生成 12 bit 的序列號?
序列號不需要全局維護(hù),在 Java 中可以使用 AtomicInteger(保證線程安全)從 0 開始自增。當(dāng)序列號超過了 4096,序列號在這一毫秒就用完了,等待下一個(gè)毫秒歸 0 重置就可以了。No.3
Snowflake 優(yōu)缺點(diǎn)
理論上 Snowflake 方案的 QPS 約為 409.6w/s(1000 * 2^12),這種分配方式可以保證在任何一個(gè) IDC 的任何一臺機(jī)器在任意毫秒內(nèi)生成的 ID 都是不同的。3.1 優(yōu)點(diǎn)
?? 毫秒數(shù)在高位,自增序列在低位,整個(gè) ID 都是趨勢遞增的。趨勢遞增的目的是:在 MySQL InnoDB 引擎中使用的是聚集索引,由于多數(shù) RDBMS 使用 B-tree 的數(shù)據(jù)結(jié)構(gòu)來存儲索引數(shù)據(jù),在主鍵的選擇上面我們應(yīng)該盡量使用有序的主鍵保證寫入性能。???不依賴數(shù)據(jù)庫等第三方系統(tǒng),以服務(wù)的方式部署,穩(wěn)定性更高,生成 ID 的性能也是非常高的。???可以根據(jù)自身業(yè)務(wù)特性分配 bit 位,非常靈活。3.2 缺點(diǎn)
???強(qiáng)依賴機(jī)器時(shí)鐘,如果機(jī)器上時(shí)鐘回?fù)?#xff0c;會導(dǎo)致發(fā)號重復(fù)或者服務(wù)會處于不可用狀態(tài)。除了時(shí)鐘回?fù)軉栴}之外,Snowflake 算法會存在并發(fā)限制,當(dāng)然對于這些問題,以本人目前的 Java 功力根本解決不了。但這并不影響我們使用它。在實(shí)際項(xiàng)目中我們可以使用基于 Snowflake 算法的開源項(xiàng)目,比如百度的 UidGenerator 或美團(tuán)的 Leaf。下面我們簡單介紹一下這兩個(gè)項(xiàng)目,感興趣的小伙伴可以自行查閱相關(guān)資料。3.3 UidGenerator
UidGenerator 是 Java 實(shí)現(xiàn)的,基于 Snowflake 算法的唯一 ID 生成器。UidGenerator 以組件形式工作在應(yīng)用項(xiàng)目中,支持自定義 workerId 位數(shù)和初始化策略,從而適用于 docker 等虛擬化環(huán)境下實(shí)例自動重啟、漂移等場景。在實(shí)現(xiàn)上,UidGenerator 通過借用未來時(shí)間來解決 sequence 天然存在的并發(fā)限制;采用 RingBuffer 來緩存已生成的 UID,并行化 UID 的生產(chǎn)和消費(fèi),同時(shí)對 CacheLine 補(bǔ)齊,避免了由 RingBuffer 帶來的硬件級「偽共享」問題。最終單機(jī) QPS 可達(dá) 600 萬。依賴版本:Java8 及以上版本,MySQL(內(nèi)置 WorkerID 分配器,啟動階段通過 DB 進(jìn)行分配;如自定義實(shí)現(xiàn),則 DB非必選依賴)。3.4 Leaf
Leaf 最早期需求是各個(gè)業(yè)務(wù)線的訂單 ID 生成需求。在美團(tuán)早期,有的業(yè)務(wù)直接通過 DB 自增的方式生成 ID,有的業(yè)務(wù)通過 Redis 緩存來生成 ID,也有的業(yè)務(wù)直接用 UUID 這種方式來生成 ID。以上的方式各自有各自的問題,因此我們決定實(shí)現(xiàn)一套分布式 ID 生成服務(wù)來滿足需求。具體 Leaf 設(shè)計(jì)文檔見:Leaf 美團(tuán)分布式ID生成服務(wù)。目前 Leaf 覆蓋了美團(tuán)點(diǎn)評公司內(nèi)部金融、餐飲、外賣、酒店旅游、貓眼電影等眾多業(yè)務(wù)線。在 4C8G VM 基礎(chǔ)上,通過公司 RPC 方式調(diào)用,QPS 壓測結(jié)果近5w/s,TP999 1ms。No.4
Snowflake 算法實(shí)現(xiàn)
在前面 Snowflake 知識的基礎(chǔ)上,現(xiàn)在我們來分析一下 Github 上 beyondfengyu 大佬基于 Java 實(shí)現(xiàn)的 SnowFlake,完整代碼如下:/**?*?twitter的snowflake算法?--?java實(shí)現(xiàn)
?*?
?*?@author?beyond
?*?@date?2016/11/26
?*/public?class?SnowFlake?{/**
?????*?起始的時(shí)間戳
?????*/private?final?static?long?START_STMP?=?1480166465631L;/**
?????*?每一部分占用的位數(shù)
?????*/private?final?static?long?SEQUENCE_BIT?=?12;?//序列號占用的位數(shù)private?final?static?long?MACHINE_BIT?=?5;???//機(jī)器標(biāo)識占用的位數(shù)private?final?static?long?DATACENTER_BIT?=?5;//數(shù)據(jù)中心占用的位數(shù)/**
?????*?每一部分的最大值
?????*/private?final?static?long?MAX_DATACENTER_NUM?=?-1L?^?(-1L?<????private?final?static?long?MAX_MACHINE_NUM?=?-1L?^?(-1L?<????private?final?static?long?MAX_SEQUENCE?=?-1L?^?(-1L?</**
?????*?每一部分向左的位移
?????*/private?final?static?long?MACHINE_LEFT?=?SEQUENCE_BIT;private?final?static?long?DATACENTER_LEFT?=?SEQUENCE_BIT?+?MACHINE_BIT;private?final?static?long?TIMESTMP_LEFT?=?DATACENTER_LEFT?+?DATACENTER_BIT;private?long?datacenterId;??//數(shù)據(jù)中心private?long?machineId;?????//機(jī)器標(biāo)識private?long?sequence?=?0L;?//序列號private?long?lastStmp?=?-1L;//上一次時(shí)間戳public?SnowFlake(long?datacenterId,?long?machineId)?{if?(datacenterId?>?MAX_DATACENTER_NUM?||?datacenterId?0)?{throw?new?IllegalArgumentException("datacenterId?can't?be?greater?than?MAX_DATACENTER_NUM?or?less?than?0");
????????}if?(machineId?>?MAX_MACHINE_NUM?||?machineId?0)?{throw?new?IllegalArgumentException("machineId?can't?be?greater?than?MAX_MACHINE_NUM?or?less?than?0");
????????}this.datacenterId?=?datacenterId;this.machineId?=?machineId;
????}/**
?????*?產(chǎn)生下一個(gè)ID
?????*
?????*?@return
?????*/public?synchronized?long?nextId()?{long?currStmp?=?getNewstmp();if?(currStmp?????????????throw?new?RuntimeException("Clock?moved?backwards.??Refusing?to?generate?id");
????????}if?(currStmp?==?lastStmp)?{//相同毫秒內(nèi),序列號自增
????????????sequence?=?(sequence?+?1)?&?MAX_SEQUENCE;//同一毫秒的序列數(shù)已經(jīng)達(dá)到最大if?(sequence?==?0L)?{
????????????????currStmp?=?getNextMill();
????????????}
????????}?else?{//不同毫秒內(nèi),序列號置為0
????????????sequence?=?0L;
????????}
????????lastStmp?=?currStmp;return?(currStmp?-?START_STMP)?<//時(shí)間戳部分
????????????????|?datacenterId?<//數(shù)據(jù)中心部分
????????????????|?machineId?<//機(jī)器標(biāo)識部分
????????????????|?sequence;?????????????????????????????//序列號部分
????}private?long?getNextMill()?{long?mill?=?getNewstmp();while?(mill?<=?lastStmp)?{
????????????mill?=?getNewstmp();
????????}return?mill;
????}private?long?getNewstmp()?{return?System.currentTimeMillis();
????}public?static?void?main(String[]?args)?{
????????SnowFlake?snowFlake?=?new?SnowFlake(2,?3);for?(int?i?=?0;?i?1?<12);?i++)?{
????????????System.out.println(snowFlake.nextId());
????????}
????}
}在詳細(xì)分析之前,我們先來回顧一下 Snowflake 算法的 ID 構(gòu)成圖:
4.1 ID 位分配
首位不用,默認(rèn)為 0。41bit(第2-42位)時(shí)間戳,是相對時(shí)間戳,通過當(dāng)前時(shí)間戳減去一個(gè)固定的歷史時(shí)間戳生成。在 SnowFlake 類定義了一個(gè) long 類型的靜態(tài)變量 START_STMP,它的值為 1480166465631L:/**?*?起始的時(shí)間戳:Sat Nov 26 2016 21:21:05 GMT+0800?(中國標(biāo)準(zhǔn)時(shí)間)
?*/private?final?static?long?START_STMP?=?1480166465631L;接著繼續(xù)定義三個(gè) long 類型的靜態(tài)變量,來表示序列號和工作機(jī)器 ID 的占用位數(shù):/**
?*?每一部分占用的位數(shù)
?*/private?final?static?long?SEQUENCE_BIT?=?12;?//序列號占用的位數(shù)private?final?static?long?MACHINE_BIT?=?5;???//機(jī)器標(biāo)識占用的位數(shù)private?final?static?long?DATACENTER_BIT?=?5;//數(shù)據(jù)中心占用的位數(shù)此外還定義了每一部分的最大值,具體如下:/**
?*?每一部分的最大值
?*/private?final?static?long?MAX_DATACENTER_NUM?=?-1L?^?(-1L?<//?31private?final?static?long?MAX_MACHINE_NUM?=?-1L?^?(-1L?<//?31private?final?static?long?MAX_SEQUENCE?=?-1L?^?(-1L?<//?4095
4.2 構(gòu)造函數(shù)
SnowFlake 類的構(gòu)造函數(shù),該構(gòu)造函數(shù)含有 datacenterId 和 machineId 兩個(gè)參數(shù),它們分別表示數(shù)據(jù)中心 id 和機(jī)器標(biāo)識:private?long?datacenterId;??//數(shù)據(jù)中心private?long?machineId;?????//機(jī)器標(biāo)識public?SnowFlake(long?datacenterId,?long?machineId)?{if?(datacenterId?>?MAX_DATACENTER_NUM?||?datacenterId?0)?{throw?new?IllegalArgumentException("datacenterId?can't?be?greater?than?MAX_DATACENTER_NUM?or?less?than?0");????????}if?(machineId?>?MAX_MACHINE_NUM?||?machineId?0)?{throw?new?IllegalArgumentException("machineId?can't?be?greater?than?MAX_MACHINE_NUM?or?less?than?0");
????????}this.datacenterId?=?datacenterId;this.machineId?=?machineId;
}
4.3 生成 id
在 SnowFlake 類的實(shí)現(xiàn)中,在創(chuàng)建完 SnowFlake 對象之后,可以通過調(diào)用 nextId 方法來獲取 ID。有的小伙伴可能對位運(yùn)算不太清楚,這里先簡單介紹一下 nextId 方法中,所用到的位運(yùn)算知識。按位與運(yùn)算符(&)參加運(yùn)算的兩個(gè)數(shù)據(jù),按二進(jìn)制位進(jìn)行 “與” 運(yùn)算,它的運(yùn)算規(guī)則:0&0=0;?0&1=0;?1&0=0;?1&1=1;即兩位同時(shí)為 1,結(jié)果才為 1,否則為 0。?? 清零:如果想將一個(gè)單元清零,只需要將它與一個(gè)各位都為零的數(shù)值相與即可。???取一個(gè)數(shù)指定位的值:若需獲取某個(gè)數(shù)指定位的值,只需把該數(shù)與指定位為 1,其余位為 0 所對應(yīng)的數(shù)相與即可。按位或運(yùn)算(|)參加運(yùn)算的兩個(gè)對象,按二進(jìn)制位進(jìn)行 “或” 運(yùn)算,它的運(yùn)算規(guī)則:0|0=0;?0|1=1;?1|0=1;?1|1=1;即僅當(dāng)兩位都為 0 時(shí),結(jié)果才為 0。左移運(yùn)算符 <<將一個(gè)運(yùn)算對象的各二進(jìn)制位全部左移若干位(左邊的二進(jìn)制位丟棄,右邊補(bǔ) 0)。若左移時(shí)舍棄的高位不包含1,則每左移一位,相當(dāng)于該數(shù)乘以 2。在了解完位運(yùn)算的相關(guān)知識后,我們再來看一下 nextId 方法的具體實(shí)現(xiàn):/**?*?產(chǎn)生下一個(gè)ID
?*
?*?@return
?*/public?synchronized?long?nextId()?{//?獲取當(dāng)前的毫秒數(shù):System.currentTimeMillis(),該方法產(chǎn)生一個(gè)當(dāng)前的毫秒,這個(gè)毫秒//?其實(shí)就是自1970年1月1日0時(shí)起的毫秒數(shù)。long?currStmp?=?getNewstmp();//?private?long?lastTimeStamp?=?-1L;?表示上一次時(shí)間戳//?檢測是否出現(xiàn)時(shí)鐘回?fù)躨f?(currStmp??????throw?new?RuntimeException("Clock?moved?backwards.??Refusing?to?generate?id");
??}//?相同毫秒內(nèi),序列號自增if?(currStmp?==?lastStmp)?{//?private?long?sequence?=?0L;?序列號//?MAX_SEQUENCE?=??????4095?111111111111//?MAX_SEQUENCE?+?1?=?4096?1000000000000
?????sequence?=?(sequence?+?1)?&?MAX_SEQUENCE;//?同一毫秒的序列數(shù)已經(jīng)達(dá)到最大if?(sequence?==?0L)?{//?阻塞到下一個(gè)毫秒,獲得新的時(shí)間戳
???????????currStmp?=?getNextMill();
???????}
?????}?else?{//不同毫秒內(nèi),序列號置為0
????????????sequence?=?0L;
???}
???lastStmp?=?currStmp;//?MACHINE_LEFT?=?SEQUENCE_BIT;?->?12//?DATA_CENTER_LEFT?=?SEQUENCE_BIT?+?MACHINE_BIT;?->?17//?TIMESTAMP_LEFT?=?DATA_CENTER_LEFT?+?DATA_CENTER_BIT;?->?22return?(currStmp?-?START_STMP)?<//時(shí)間戳部分
????????????????|?datacenterId?<//數(shù)據(jù)中心部分
????????????????|?machineId?<//機(jī)器標(biāo)識部分
????????????????|?sequence;?????????????????????????????//序列號部分
}現(xiàn)在我們來看一下使用方式:public?static?void?main(String[]?args)?{
??SnowFlake?snowFlake?=?new?SnowFlake(2,?3);for?(int?i?=?0;?i?1?<12);?i++)?{
????System.out.println(snowFlake.nextId());
??}
}現(xiàn)在我們已經(jīng)可以利用 SnowFlake 對象生成唯一 ID 了,那這個(gè)唯一 ID 有什么用呢?這里舉一個(gè)簡單的應(yīng)用場景,即基于 SnowFlake 的短網(wǎng)址生成器,其主要思路是使用 SnowFlake 算法生成一個(gè)整數(shù),然后對該整數(shù)進(jìn)行 62 進(jìn)制編碼最終生成一個(gè)短地址 URL。對短網(wǎng)址生成器感興趣的小伙伴,可以參考徐劉根大佬在碼云上分享的工具類。最后我們來簡單總結(jié)一下,本文主要介紹了什么是 Snowflake(雪花)算法、Snowflake 算法 ID 構(gòu)成圖及其優(yōu)缺點(diǎn),最后詳細(xì)分析了 Github 上 beyondfengyu 大佬基于 Java 實(shí)現(xiàn)的 SnowFlake。在實(shí)際項(xiàng)目中,建議大家選用基于 Snowflake 算法成熟的開源項(xiàng)目,如百度的 UidGenerator 或美團(tuán)的 Leaf。
參考資源:
?? Twitter-ids:https://developer.twitter.com/en/docs/basics/twitter-ids???MDN - BigInt:https://developer.mozilla.org/zh-CN/docs/Web/JavaScript/Reference/Global_Objects/BigInt???Leaf:美團(tuán)分布式ID生成服務(wù)開源:https://tech.meituan.com/2019/03/07/open-source-project-leaf.html???漫漫路 - Twitter-Snowflake,64位自增ID算法詳解:https://www.lanindex.com/twitter-snowflake-?END - 創(chuàng)作挑戰(zhàn)賽新人創(chuàng)作獎(jiǎng)勵(lì)來咯,堅(jiān)持創(chuàng)作打卡瓜分現(xiàn)金大獎(jiǎng)總結(jié)
以上是生活随笔為你收集整理的java生成唯一有序序列号_分布式唯一 ID 之 Snowflake 算法的全部內(nèi)容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: linux类型有哪些(linux类型)
- 下一篇: 蜂鸣器音乐代码 天空之城_潮玩 | 艺术