<kbd id="afajh"><form id="afajh"></form></kbd>
<strong id="afajh"><dl id="afajh"></dl></strong>
    <del id="afajh"><form id="afajh"></form></del>
        1. <th id="afajh"><progress id="afajh"></progress></th>
          <b id="afajh"><abbr id="afajh"></abbr></b>
          <th id="afajh"><progress id="afajh"></progress></th>

          分布式全局唯一ID生成策略及算法

          共 3778字,需瀏覽 8分鐘

           ·

          2021-09-16 13:38

          點(diǎn)擊上方 Java學(xué)習(xí)之道,選擇 設(shè)為星標(biāo)

          每天18:30點(diǎn),干貨準(zhǔn)時(shí)奉上!

          來(lái)源: blog.csdn.net/CharlesYooSky/article/details/120075054

          Part1全局唯一id介紹

          系統(tǒng)唯一id是我們?cè)谠O(shè)計(jì)階段常常遇到的問(wèn)題。在復(fù)雜的分布式系統(tǒng)中,幾乎都需要對(duì)大量的數(shù)據(jù)和消息進(jìn)行唯一標(biāo)識(shí)。在設(shè)計(jì)初期,我們需要考慮日后數(shù)據(jù)量的級(jí)別,如果可能會(huì)對(duì)數(shù)據(jù)進(jìn)行分庫(kù)分表,那么就需要有一個(gè)全局唯一id來(lái)標(biāo)識(shí)一條數(shù)據(jù)或記錄。生成唯一id的策略有多種,但是每種策略都有它的適用場(chǎng)景、優(yōu)點(diǎn)以及局限性。

          全局唯一id特點(diǎn):

          • 全局唯一性:不能出現(xiàn)重復(fù)的ID號(hào),既然是唯一標(biāo)識(shí),這是最基本的要求;
          • 趨勢(shì)遞增:在MySQL InnoDB引擎中使用的是聚集索引,由于多數(shù)RDBMS使用B-tree的數(shù)據(jù)結(jié)構(gòu)來(lái)存儲(chǔ)索引數(shù)據(jù),在主鍵的選擇上面我們應(yīng)該盡量使用有序的主鍵保證寫(xiě)入性能;
          • 單調(diào)遞增:保證下一個(gè)ID一定大于上一個(gè)ID,例如事務(wù)版本號(hào)、IM增量消息、排序等特殊需求;
          • 信息安全:如果ID是連續(xù)的,惡意用戶(hù)的扒取工作就非常容易做了,直接按照順序下載指定URL即可;如果是訂單號(hào)就更危險(xiǎn)了,競(jìng)對(duì)可以直接知道我們一天的單量。所以在一些應(yīng)用場(chǎng)景下,會(huì)需要ID無(wú)規(guī)則、不規(guī)則;
          • 高可用性:同時(shí)除了對(duì)ID號(hào)碼自身的要求,業(yè)務(wù)還對(duì)ID號(hào)生成系統(tǒng)的可用性要求極高,想象一下,如果ID生成系統(tǒng)癱瘓,這就會(huì)帶來(lái)一場(chǎng)災(zāi)難。所以不能有單點(diǎn)故障;
          • 分片支持:可以控制ShardingId。比如某一個(gè)用戶(hù)的文章要放在同一個(gè)分片內(nèi),這樣查詢(xún)效率高,修改也容易;
          • 長(zhǎng)度適中。

          Part2常見(jiàn)全局唯一id生成策略

          1、數(shù)據(jù)庫(kù)自增長(zhǎng)序列或字段生成id

          最常見(jiàn)的一種生成id方式。利用數(shù)據(jù)庫(kù)本身來(lái)進(jìn)行設(shè)置,在全數(shù)據(jù)庫(kù)內(nèi)保持唯一。

          【優(yōu)點(diǎn)】

          非常簡(jiǎn)單。利用現(xiàn)有數(shù)據(jù)庫(kù)系統(tǒng)的功能實(shí)現(xiàn),成本小,代碼簡(jiǎn)單,性能可以接受。ID號(hào)單調(diào)遞增。可以實(shí)現(xiàn)一些對(duì)ID有特殊要求的業(yè)務(wù),比如對(duì)分頁(yè)或者排序結(jié)果這類(lèi)需求有幫助。

          【缺點(diǎn)】

          1. 強(qiáng)依賴(lài)DB。不同數(shù)據(jù)庫(kù)語(yǔ)法和實(shí)現(xiàn)不同,數(shù)據(jù)庫(kù)遷移的時(shí)候、多數(shù)據(jù)庫(kù)版本支持的時(shí)候、或分表分庫(kù)的時(shí)候需要處理,會(huì)比較麻煩。當(dāng)DB異常時(shí)整個(gè)系統(tǒng)不可用,屬于致命問(wèn)題。
          2. 單點(diǎn)故障。在單個(gè)數(shù)據(jù)庫(kù)或讀寫(xiě)分離或一主多從的情況下,只有一個(gè)主庫(kù)可以生成。有單點(diǎn)故障的風(fēng)險(xiǎn)。
          3. 數(shù)據(jù)一致性問(wèn)題。配置主從復(fù)制可以盡可能的增加可用性,但是數(shù)據(jù)一致性在特殊情況下難以保證。主從切換時(shí)的不一致可能會(huì)導(dǎo)致重復(fù)發(fā)號(hào)。
          4. 難于擴(kuò)展。在性能達(dá)不到要求的情況下,比較難于擴(kuò)展。ID發(fā)號(hào)性能瓶頸限制在單臺(tái)MySQL的讀寫(xiě)性能。

          【部分優(yōu)化方案】

          針對(duì)主庫(kù)單點(diǎn), 如果有多個(gè)Master庫(kù),則每個(gè)Master庫(kù)設(shè)置的起始數(shù)字不一樣,步長(zhǎng)一樣,可以是Master的個(gè)數(shù)。比如:Master1 生成的是 1,4,7,10,Master2生成的是2,5,8,11 Master3生成的是 3,6,9,12。這樣就可以有效生成集群中的唯一ID,也可以大大降低ID生成數(shù)據(jù)庫(kù)操作的負(fù)載。

          2、UUID

          常見(jiàn)的生成id方式,利用程序生成。

          UUID (Universally Unique Identifier) 的目的,是讓分布式系統(tǒng)中的所有元素,都能有唯一的辨識(shí)資訊,而不需要透過(guò)中央控制端來(lái)做辨識(shí)資訊的指定。如此一來(lái),每個(gè)人都可以建立不與其它人沖突的 UUID。在這樣的情況下,就不需考慮數(shù)據(jù)庫(kù)建立時(shí)的名稱(chēng)重復(fù)問(wèn)題。

          UUID的標(biāo)準(zhǔn)形式包含32個(gè)16進(jìn)制數(shù)字,以連字號(hào)分為五段,形式為8-4-4-4-12的36個(gè)字符,示例:550e8400-e29b-41d4-a716-446655440000,到目前為止業(yè)界一共有5種方式生成UUID。

          在Java中我們可以直接使用下面的API生成UUID:

          UUID uuid  =  UUID.randomUUID(); 
          String s = UUID.randomUUID().toString();

          【優(yōu)點(diǎn)】

          • 非常簡(jiǎn)單,本地生成,代碼方便,API調(diào)用方便。
          • 性能非高。生成的id性能非常好,沒(méi)有網(wǎng)絡(luò)消耗,基本不會(huì)有性能問(wèn)題。
          • 全球唯一。在數(shù)據(jù)庫(kù)遷移、系統(tǒng)數(shù)據(jù)合并、或者數(shù)據(jù)庫(kù)變更的情況下,可以 從容應(yīng)對(duì)。

          【缺點(diǎn)】

          • 存儲(chǔ)成本高。UUID太長(zhǎng),16字節(jié)128位,通常以36長(zhǎng)度的字符串表示,很多場(chǎng)景不適用。如果是海量數(shù)據(jù)庫(kù),就需要考慮存儲(chǔ)量的問(wèn)題。
          • 信息不安全。基于MAC地址生成UUID的算法可能會(huì)造成MAC地址泄露,這個(gè)漏洞曾被用于尋找梅麗莎病毒的制作者位置。
          • 不適用作為主鍵,ID作為主鍵時(shí)在特定的環(huán)境會(huì)存在一些問(wèn)題,比如做DB主鍵的場(chǎng)景下,UUID就非常不適用。UUID往往是使用字符串存儲(chǔ),查詢(xún)的效率比較低。
          • UUID是無(wú)序的。不是單調(diào)遞增的,而現(xiàn)階段主流的數(shù)據(jù)庫(kù)主鍵索引都是選用的B+樹(shù)索引,對(duì)于無(wú)序長(zhǎng)度過(guò)長(zhǎng)的主鍵插入效率比較低。
          • 傳輸數(shù)據(jù)量大
          • 不可讀

          【部分優(yōu)化方案】

          • 為了解決UUID不可讀, 可以使用UUID to Int64的方法 。
          • 為了解決UUID無(wú)序的問(wèn)題, NHibernate在其主鍵生成方式中提供了Comb算法(combined guid/timestamp)。保留GUID的10個(gè)字節(jié),用另6個(gè)字節(jié)表示GUID生成的時(shí)間(DateTime)。

          3、Redis生成ID

          當(dāng)使用數(shù)據(jù)庫(kù)來(lái)生成ID性能不夠要求的時(shí)候,我們可以嘗試使用Redis來(lái)生成ID。這主要依賴(lài)于Redis是單線(xiàn)程的,所以也可以用生成全局唯一的ID。可以用Redis的原子操作 INCR和INCRBY來(lái)實(shí)現(xiàn)。

          可以使用Redis集群來(lái)獲取更高的吞吐量。假如一個(gè)集群中有5臺(tái)Redis。可以初始化每臺(tái)Redis的值分別是1,2,3,4,5,然后步長(zhǎng)都是5。各個(gè)Redis生成的ID為:

          • A:1,6,11,16,21
          • B:2,7,12,17,22
          • C:3,8,13,18,23
          • D:4,9,14,19,24
          • E:5,10,15,20,25

          這個(gè)負(fù)載到哪臺(tái)機(jī)器上需要提前設(shè)定好,未來(lái)很難做修改。但是3-5臺(tái)服務(wù)器基本能夠滿(mǎn)足,都可以獲得不同的ID。步長(zhǎng)和初始值一定需要事先設(shè)定好。使用Redis集群也可以防止單點(diǎn)故障的問(wèn)題。

          比較適合使用Redis來(lái)生成日切流水號(hào)。比如訂單號(hào)=日期+當(dāng)日自增長(zhǎng)號(hào)。可以每天在Redis中生成一個(gè)Key,使用INCR進(jìn)行累加。

          【優(yōu)點(diǎn)】

          • 不依賴(lài)于數(shù)據(jù)庫(kù),靈活方便,且性能優(yōu)于數(shù)據(jù)庫(kù)。。
          • 數(shù)字ID天然排序,對(duì)分頁(yè)或者需要排序的結(jié)果很有幫助。

          【缺點(diǎn)】

          • 如果系統(tǒng)中沒(méi)有Redis,還需要引入新的組件,增加系統(tǒng)復(fù)雜度。。
          • 需要編碼和配置的工作量比較大。
          • Redis單點(diǎn)故障,影響序列服務(wù)的可用性。

          4、zookeeper生成ID

          zookeeper主要通過(guò)其znode數(shù)據(jù)版本來(lái)生成序列號(hào),可以生成32位和64位的數(shù)據(jù)版本號(hào),客戶(hù)端可以使用這個(gè)版本號(hào)來(lái)作為唯一的序列號(hào)。

          很少會(huì)使用zookeeper來(lái)生成唯一ID。主要是由于需要依賴(lài)zookeeper,并且是多步調(diào)用API,如果在競(jìng)爭(zhēng)較大的情況下,需要考慮使用分布式鎖。因此,性能在高并發(fā)的分布式環(huán)境下,也不甚理想。

          5、Twitter的snowflake算法

          snowflake(雪花算法)是Twitter開(kāi)源的分布式ID生成算法,結(jié)果是一個(gè)long型的ID。這種方案把64-bit分別劃分成多段,分開(kāi)來(lái)標(biāo)示機(jī)器、時(shí)間等。如圖:

          其核心思想是:使用41bit作為毫秒數(shù),10bit作為機(jī)器的ID(5個(gè)bit是數(shù)據(jù)中心,5個(gè)bit的機(jī)器ID),12bit作為毫秒內(nèi)的流水號(hào)(意味著每個(gè)節(jié)點(diǎn)在每毫秒可以產(chǎn)生 4096 個(gè) ID),最后還有一個(gè)符號(hào)位,永遠(yuǎn)是0。具體實(shí)現(xiàn)的代碼可以參看github。

          snowflake算法可以根據(jù)自身項(xiàng)目的需要進(jìn)行一定的修改。比如估算未來(lái)的數(shù)據(jù)中心個(gè)數(shù),每個(gè)數(shù)據(jù)中心的機(jī)器數(shù)以及統(tǒng)一毫秒可以能的并發(fā)數(shù)來(lái)調(diào)整在算法中所需要的bit數(shù)。

          【優(yōu)點(diǎn)】

          • 穩(wěn)定性高,不依賴(lài)于數(shù)據(jù)庫(kù)等第三方系統(tǒng),以服務(wù)的方式部署,穩(wěn)定性更高,生成ID的性能也是非常高的。
          • 靈活方便,可以根據(jù)自身業(yè)務(wù)特性分配bit位。
          • 單機(jī)上ID單調(diào)自增,毫秒數(shù)在高位,自增序列在低位,整個(gè)ID都是趨勢(shì)遞增的。

          【缺點(diǎn)】

          • 強(qiáng)依賴(lài)機(jī)器時(shí)鐘,如果機(jī)器上時(shí)鐘回?fù)埽瑫?huì)導(dǎo)致發(fā)號(hào)重復(fù)或者服務(wù)會(huì)處于不可用狀態(tài)。
          • ID可能不是全局遞增。在單機(jī)上是遞增的,但是由于涉及到分布式環(huán)境,每臺(tái)機(jī)器上的時(shí)鐘不可能完全同步,也許有時(shí)候也會(huì)出現(xiàn)不是全局遞增的情況。
          -- END --

           | 更多精彩文章 -



          加我微信,交個(gè)朋友
          長(zhǎng)按/掃碼添加↑↑↑

          瀏覽 62
          點(diǎn)贊
          評(píng)論
          收藏
          分享

          手機(jī)掃一掃分享

          分享
          舉報(bào)
          評(píng)論
          圖片
          表情
          推薦
          點(diǎn)贊
          評(píng)論
          收藏
          分享

          手機(jī)掃一掃分享

          分享
          舉報(bào)
          <kbd id="afajh"><form id="afajh"></form></kbd>
          <strong id="afajh"><dl id="afajh"></dl></strong>
            <del id="afajh"><form id="afajh"></form></del>
                1. <th id="afajh"><progress id="afajh"></progress></th>
                  <b id="afajh"><abbr id="afajh"></abbr></b>
                  <th id="afajh"><progress id="afajh"></progress></th>
                  欧美高清精品成人在线 | 男女视频网址 | 国产丝袜足交在线 | 亚洲青娱乐精品91 | 青青草视频在线观看 |