日韩性视频-久久久蜜桃-www中文字幕-在线中文字幕av-亚洲欧美一区二区三区四区-撸久久-香蕉视频一区-久久无码精品丰满人妻-国产高潮av-激情福利社-日韩av网址大全-国产精品久久999-日本五十路在线-性欧美在线-久久99精品波多结衣一区-男女午夜免费视频-黑人极品ⅴideos精品欧美棵-人人妻人人澡人人爽精品欧美一区-日韩一区在线看-欧美a级在线免费观看

歡迎訪問 生活随笔!

生活随笔

當前位置: 首頁 > 编程资源 > 编程问答 >内容正文

编程问答

java生成唯一有序序列号_分布式唯一 ID 之 Snowflake 算法

發布時間:2023/12/3 编程问答 26 豆豆
生活随笔 收集整理的這篇文章主要介紹了 java生成唯一有序序列号_分布式唯一 ID 之 Snowflake 算法 小編覺得挺不錯的,現在分享給大家,幫大家做個參考.

SegmentFault 社區專欄:全棧修仙之路作者:semlinker


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(雪花)是一項服務,用于為 Twitter 內的對象(推文,直接消息,用戶,集合,列表等)生成唯一的 ID。這些 IDs 是唯一的 64 位無符號整數,它們基于時間,而不是順序的。完整的 ID 由時間戳,工作機器編號和序列號組成。當在 API 中使用 JSON 數據格式時,請務必始終使用 id_str 字段而不是 id,這一點很重要。這是由于處理 JSON 的 Javascript 和其他語言計算大整數的方式造成的。如果你遇到 id 和 id_str 似乎不匹配的情況,這是因為你的環境已經解析了 id 整數,并在處理的過程中仔細分析了這個數字。在 JavaScript 中,Number 基本類型可以精確表示的最大整數是 2^53。因此如果直接使用 Number 來表示 64 位的 Snowflake ID 肯定是行不通的。所以 Twitter 工程師們讓我們務必使用 id_str 字段即通過字符串來表示生成的 ID。當然這個問題不僅僅存在于使用 Snowflake ID 的場景,為了解決 JavaScript 不能安全存儲和操作大整數的問題,BigInt 這個救星出現了,它是一種內置對象,可以表示大于 2^53 的整數,甚至是任意大的整數。BigInt 現在處在 ECMAScript 標準化過程中的第三階段。當它進入第四階段草案,也就是最終標準時,BigInt 將成為 Javacript 中的第二種內置數值類型。BigInt 可能會成為自 ES2015 引入 Symbol 之后,增加的第一個新的內置類型。

1.2 Snowflake 算法

下圖是 Snowflake 算法的 ID 構成圖:???1 位標識部分,該位不用主要是為了保持 ID 的自增特性,若使用了最高位,int64_t 會表示為負數。在 Java 中由于 long 類型的最高位是符號位,正數是 0,負數是 1,一般生成的 ID 為正整數,所以最高位為 0;???41 位時間戳部分,這個是毫秒級的時間,一般實現上不會存儲當前的時間戳,而是時間戳的差值(當前時間減去固定的開始時間),這樣可以使產生的 ID 從更小值開始;41 位的時間戳可以使用 69 年,(1L << 41) / (1000L 60 60 24 365) = (2199023255552 / 31536000000) ≈ 69.73 年;???10 位工作機器 ID 部分,Twitter 實現中使用前 5 位作為數據中心標識,后 5 位作為機器標識,可以部署 1024 (2^10)個節點;???12 位序列號部分,支持同一毫秒內同一個節點可以生成 4096 (2^12)個 ID;Snowflake 算法生成的 ID 大致上是按照時間遞增的,用在分布式系統中時,需要注意數據中心標識和機器標識必須唯一,這樣就能保證每個節點生成的 ID 都是唯一的。我們不一定需要像 Twitter 那樣使用 5 位作為數據中心標識,另 5 位作為機器標識,可以根據我們業務的需要,靈活分配工作機器 ID 部分。比如:若不需要數據中心,完全可以使用全部 10 位作為機器標識;若數據中心不多,也可以只使用 3 位作為數據中心,7 位作為機器標識。

No.2

Snowflake 解惑

以下問題來源于漫漫路博客 - “Twitter-Snowflake,64 位自增ID算法詳解” 評論區

2.1 既然是 64 位,為何第一位不使用?

首位不用主要是為了保持 ID 的自增特性,若使用了最高位,int64_t 會表示為負數。在 Java 中由于 long 類型的最高位是符號位,正數是 0,負數是 1,一般生成的 ID 為正整數,所以最高位為 0。

2.2 怎么生成 41 位的時間戳?

41 位的時間戳,這個是毫秒級的時間,一般實現上不會存儲當前的時間戳,而是時間戳的差值(當前時間減去固定的開始時間)。41 位只是預留位(主要目的是約定使用年限,固定的開始時間),不用的位數填 0 就好了。

2.3 工作機器 id 如果使用 MAC 地址的話,怎么轉成 10 bit?

網絡中每臺設備都有一個唯一的網絡標識,這個地址叫 MAC 地址或網卡地址,由網絡設備制造商生產時寫在硬件內部。MAC 地址則是 48 位的(6 個字節),通常表示為 12 個 16 進制數,每 2 個 16 進制數之間用冒號隔開,如08:00:20:0A:8C:6D 就是一個 MAC 地址。具體如下圖所示,其前 3 字節表示OUI(Organizationally Unique Identifier),是 IEEE (電氣和電子工程師協會)區分不同的廠家,后 3 字節由廠家自行分配。(圖片來源 - 百度百科)很明顯 Mac 地址是 48 位,而我們的工作機器 ID 部分只有 10 位,因此并不能直接使用 Mac 地址作為工作機器 ID。若要選用 Mac 地址的話,還需使用一個額外的工作機器 ID 分配器,用來實現 ID 與 Mac 地址間的唯一映射。

2.4 怎么生成 12 bit 的序列號?

序列號不需要全局維護,在 Java 中可以使用 AtomicInteger(保證線程安全)從 0 開始自增。當序列號超過了 4096,序列號在這一毫秒就用完了,等待下一個毫秒歸 0 重置就可以了。

No.3

Snowflake 優缺點

理論上 Snowflake 方案的 QPS 約為 409.6w/s(1000 * 2^12),這種分配方式可以保證在任何一個 IDC 的任何一臺機器在任意毫秒內生成的 ID 都是不同的。

3.1 優點

?? 毫秒數在高位,自增序列在低位,整個 ID 都是趨勢遞增的。趨勢遞增的目的是:在 MySQL InnoDB 引擎中使用的是聚集索引,由于多數 RDBMS 使用 B-tree 的數據結構來存儲索引數據,在主鍵的選擇上面我們應該盡量使用有序的主鍵保證寫入性能。???不依賴數據庫等第三方系統,以服務的方式部署,穩定性更高,生成 ID 的性能也是非常高的。???可以根據自身業務特性分配 bit 位,非常靈活。

3.2 缺點

???強依賴機器時鐘,如果機器上時鐘回撥,會導致發號重復或者服務會處于不可用狀態。除了時鐘回撥問題之外,Snowflake 算法會存在并發限制,當然對于這些問題,以本人目前的 Java 功力根本解決不了。但這并不影響我們使用它。在實際項目中我們可以使用基于 Snowflake 算法的開源項目,比如百度的 UidGenerator 或美團的 Leaf。下面我們簡單介紹一下這兩個項目,感興趣的小伙伴可以自行查閱相關資料。

3.3 UidGenerator

UidGenerator 是 Java 實現的,基于 Snowflake 算法的唯一 ID 生成器。UidGenerator 以組件形式工作在應用項目中,支持自定義 workerId 位數和初始化策略,從而適用于 docker 等虛擬化環境下實例自動重啟、漂移等場景。在實現上,UidGenerator 通過借用未來時間來解決 sequence 天然存在的并發限制;采用 RingBuffer 來緩存已生成的 UID,并行化 UID 的生產和消費,同時對 CacheLine 補齊,避免了由 RingBuffer 帶來的硬件級「偽共享」問題。最終單機 QPS 可達 600 萬。依賴版本:Java8 及以上版本,MySQL(內置 WorkerID 分配器,啟動階段通過 DB 進行分配;如自定義實現,則 DB非必選依賴)。

3.4 Leaf

Leaf 最早期需求是各個業務線的訂單 ID 生成需求。在美團早期,有的業務直接通過 DB 自增的方式生成 ID,有的業務通過 Redis 緩存來生成 ID,也有的業務直接用 UUID 這種方式來生成 ID。以上的方式各自有各自的問題,因此我們決定實現一套分布式 ID 生成服務來滿足需求。具體 Leaf 設計文檔見:Leaf 美團分布式ID生成服務。目前 Leaf 覆蓋了美團點評公司內部金融、餐飲、外賣、酒店旅游、貓眼電影等眾多業務線。在 4C8G VM 基礎上,通過公司 RPC 方式調用,QPS 壓測結果近5w/s,TP999 1ms。

No.4

Snowflake 算法實現

在前面 Snowflake 知識的基礎上,現在我們來分析一下 Github 上 beyondfengyu 大佬基于 Java 實現的 SnowFlake,完整代碼如下:/**
?*?twitter的snowflake算法?--?java實現
?*?
?*?@author?beyond
?*?@date?2016/11/26
?*/public?class?SnowFlake?{/**
?????*?起始的時間戳
?????*/private?final?static?long?START_STMP?=?1480166465631L;/**
?????*?每一部分占用的位數
?????*/private?final?static?long?SEQUENCE_BIT?=?12;?//序列號占用的位數private?final?static?long?MACHINE_BIT?=?5;???//機器標識占用的位數private?final?static?long?DATACENTER_BIT?=?5;//數據中心占用的位數/**
?????*?每一部分的最大值
?????*/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;??//數據中心private?long?machineId;?????//機器標識private?long?sequence?=?0L;?//序列號private?long?lastStmp?=?-1L;//上一次時間戳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;
????}/**
?????*?產生下一個ID
?????*
?????*?@return
?????*/public?synchronized?long?nextId()?{long?currStmp?=?getNewstmp();if?(currStmp?????????????throw?new?RuntimeException("Clock?moved?backwards.??Refusing?to?generate?id");
????????}if?(currStmp?==?lastStmp)?{//相同毫秒內,序列號自增
????????????sequence?=?(sequence?+?1)?&?MAX_SEQUENCE;//同一毫秒的序列數已經達到最大if?(sequence?==?0L)?{
????????????????currStmp?=?getNextMill();
????????????}
????????}?else?{//不同毫秒內,序列號置為0
????????????sequence?=?0L;
????????}
????????lastStmp?=?currStmp;return?(currStmp?-?START_STMP)?<//時間戳部分
????????????????|?datacenterId?<//數據中心部分
????????????????|?machineId?<//機器標識部分
????????????????|?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());
????????}
????}
}在詳細分析之前,我們先來回顧一下 Snowflake 算法的 ID 構成圖:

4.1 ID 位分配

首位不用,默認為 0。41bit(第2-42位)時間戳,是相對時間戳,通過當前時間戳減去一個固定的歷史時間戳生成。在 SnowFlake 類定義了一個 long 類型的靜態變量 START_STMP,它的值為 1480166465631L:/**
?*?起始的時間戳:Sat Nov 26 2016 21:21:05 GMT+0800?(中國標準時間)
?*/private?final?static?long?START_STMP?=?1480166465631L;接著繼續定義三個 long 類型的靜態變量,來表示序列號和工作機器 ID 的占用位數:/**
?*?每一部分占用的位數
?*/private?final?static?long?SEQUENCE_BIT?=?12;?//序列號占用的位數private?final?static?long?MACHINE_BIT?=?5;???//機器標識占用的位數private?final?static?long?DATACENTER_BIT?=?5;//數據中心占用的位數此外還定義了每一部分的最大值,具體如下:/**
?*?每一部分的最大值
?*/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 構造函數

SnowFlake 類的構造函數,該構造函數含有 datacenterId 和 machineId 兩個參數,它們分別表示數據中心 id 和機器標識:private?long?datacenterId;??//數據中心private?long?machineId;?????//機器標識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 類的實現中,在創建完 SnowFlake 對象之后,可以通過調用 nextId 方法來獲取 ID。有的小伙伴可能對位運算不太清楚,這里先簡單介紹一下 nextId 方法中,所用到的位運算知識。按位與運算符(&)參加運算的兩個數據,按二進制位進行 “與” 運算,它的運算規則:0&0=0;?0&1=0;?1&0=0;?1&1=1;即兩位同時為 1,結果才為 1,否則為 0。?? 清零:如果想將一個單元清零,只需要將它與一個各位都為零的數值相與即可。???取一個數指定位的值:若需獲取某個數指定位的值,只需把該數與指定位為 1,其余位為 0 所對應的數相與即可。按位或運算(|)參加運算的兩個對象,按二進制位進行 “或” 運算,它的運算規則:0|0=0;?0|1=1;?1|0=1;?1|1=1;即僅當兩位都為 0 時,結果才為 0。左移運算符 <<將一個運算對象的各二進制位全部左移若干位(左邊的二進制位丟棄,右邊補 0)。若左移時舍棄的高位不包含1,則每左移一位,相當于該數乘以 2。在了解完位運算的相關知識后,我們再來看一下 nextId 方法的具體實現:/**
?*?產生下一個ID
?*
?*?@return
?*/public?synchronized?long?nextId()?{//?獲取當前的毫秒數:System.currentTimeMillis(),該方法產生一個當前的毫秒,這個毫秒//?其實就是自1970年1月1日0時起的毫秒數。long?currStmp?=?getNewstmp();//?private?long?lastTimeStamp?=?-1L;?表示上一次時間戳//?檢測是否出現時鐘回撥if?(currStmp??????throw?new?RuntimeException("Clock?moved?backwards.??Refusing?to?generate?id");
??}//?相同毫秒內,序列號自增if?(currStmp?==?lastStmp)?{//?private?long?sequence?=?0L;?序列號//?MAX_SEQUENCE?=??????4095?111111111111//?MAX_SEQUENCE?+?1?=?4096?1000000000000
?????sequence?=?(sequence?+?1)?&?MAX_SEQUENCE;//?同一毫秒的序列數已經達到最大if?(sequence?==?0L)?{//?阻塞到下一個毫秒,獲得新的時間戳
???????????currStmp?=?getNextMill();
???????}
?????}?else?{//不同毫秒內,序列號置為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)?<//時間戳部分
????????????????|?datacenterId?<//數據中心部分
????????????????|?machineId?<//機器標識部分
????????????????|?sequence;?????????????????????????????//序列號部分
}現在我們來看一下使用方式: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());
??}
}現在我們已經可以利用 SnowFlake 對象生成唯一 ID 了,那這個唯一 ID 有什么用呢?這里舉一個簡單的應用場景,即基于 SnowFlake 的短網址生成器,其主要思路是使用 SnowFlake 算法生成一個整數,然后對該整數進行 62 進制編碼最終生成一個短地址 URL。對短網址生成器感興趣的小伙伴,可以參考徐劉根大佬在碼云上分享的工具類。最后我們來簡單總結一下,本文主要介紹了什么是 Snowflake(雪花)算法、Snowflake 算法 ID 構成圖及其優缺點,最后詳細分析了 Github 上 beyondfengyu 大佬基于 Java 實現的 SnowFlake。在實際項目中,建議大家選用基于 Snowflake 算法成熟的開源項目,如百度的 UidGenerator 或美團的 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:美團分布式ID生成服務開源:https://tech.meituan.com/2019/03/07/open-source-project-leaf.html???漫漫路 - Twitter-Snowflake,64位自增ID算法詳解:https://www.lanindex.com/twitter-snowflake-?END - 創作挑戰賽新人創作獎勵來咯,堅持創作打卡瓜分現金大獎

總結

以上是生活随笔為你收集整理的java生成唯一有序序列号_分布式唯一 ID 之 Snowflake 算法的全部內容,希望文章能夠幫你解決所遇到的問題。

如果覺得生活随笔網站內容還不錯,歡迎將生活随笔推薦給好友。