<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生成系統(tǒng)

          共 4618字,需瀏覽 10分鐘

           ·

          2020-07-30 18:33

          程序員的成長之路
          互聯(lián)網(wǎng)/程序員/技術(shù)/資料共享?
          關(guān)注


          閱讀本文大概需要 7 分鐘。

          來自:網(wǎng)絡(luò)

          ID Generator id生成器 分布式id生成系統(tǒng),簡單易用、高性能、高可用的id生成系統(tǒng)

          簡介

          Tinyid是用Java開發(fā)的一款分布式id生成系統(tǒng),基于數(shù)據(jù)庫號段算法實現(xiàn),關(guān)于這個算法可以參考美團(tuán)leaf或者tinyid原理介紹。
          Tinyid擴(kuò)展了leaf-segment算法,支持了多db(master),同時提供了java-client(sdk)使id生成本地化,獲得了更好的性能與可用性。
          Tinyid在滴滴客服部門使用,均通過tinyid-client方式接入,每天生成億級別的id。

          tinyid系統(tǒng)架構(gòu)圖

          下面是一些關(guān)于這個架構(gòu)圖的說明:
          • nextId和getNextSegmentId是tinyid-server對外提供的兩個http接口


          • nextId是獲取下一個id,當(dāng)調(diào)用nextId時,會傳入bizType,每個bizType的id數(shù)據(jù)是隔離的,生成id會使用該bizType類型生成的IdGenerator。


          • getNextSegmentId是獲取下一個可用號段,tinyid-client會通過此接口來獲取可用號段


          • IdGenerator是id生成的接口


          • IdGeneratorFactory是生產(chǎn)具體IdGenerator的工廠,每個biz_type生成一個IdGenerator實例。通過工廠,我們可以隨時在db中新增biz_type,而不用重啟服務(wù)


          • IdGeneratorFactory實際上有兩個子類IdGeneratorFactoryServer和IdGeneratorFactoryClient,區(qū)別在于,getNextSegmentId的不同,一個是DbGet,一個是HttpGet


          • CachedIdGenerator則是具體的id生成器對象,持有currentSegmentId和nextSegmentId對象,負(fù)責(zé)nextId的核心流程。

            nextId最終通過AtomicLong.andAndGet(delta)方法產(chǎn)生。

          性能與可用性

          性能

          1. http方式訪問,性能取決于http server的能力,網(wǎng)絡(luò)傳輸速度
          2. java-client方式,id為本地生成,號段長度(step)越長,qps越大,如果將號段設(shè)置足夠大,則qps可達(dá)1000w+

          可用性

          1. 依賴db,當(dāng)db不可用時,因為server有緩存,所以還可以使用一段時間,如果配置了多個db,則只要有1個db存活,則服務(wù)可用
          2. 使用tiny-client,只要server有一臺存活,則理論上可用,server全掛,因為client有緩存,也可以繼續(xù)使用一段時間

          Tinyid的特性

          1. 全局唯一的long型id
          2. 趨勢遞增的id,即不保證下一個id一定比上一個大
          3. 非連續(xù)性
          4. 提供http和java client方式接入
          5. 支持批量獲取id
          6. 支持生成1,3,5,7,9…序列的id
          7. 支持多個db的配置,無單點
          適用場景:只關(guān)心id是數(shù)字,趨勢遞增的系統(tǒng),可以容忍id不連續(xù),有浪費的場景 不適用場景:類似訂單id的業(yè)務(wù)(因為生成的id大部分是連續(xù)的,容易被掃庫、或者測算出訂單量)

          推薦使用方式

          • tinyid-server推薦部署到多個機(jī)房的多臺機(jī)器

            • 多機(jī)房部署可用性更高,http方式訪問需使用方考慮延遲問題

          • 推薦使用tinyid-client來獲取id,好處如下:

            • id為本地生成(調(diào)用AtomicLong.addAndGet方法),性能大大增加


            • client對server訪問變的低頻,減輕了server的壓力


            • 因為低頻,即便client使用方和server不在一個機(jī)房,也無須擔(dān)心延遲


            • 即便所有server掛掉,因為client預(yù)加載了號段,依然可以繼續(xù)使用一段時間 注:使用tinyid-client方式,如果client機(jī)器較多頻繁重啟,可能會浪費較多的id,這時可以考慮使用http方式

          • 推薦db配置兩個或更多:

            • db配置多個時,只要有1個db存活,則服務(wù)可用 多db配置,如配置了兩個db,則每次新增業(yè)務(wù)需在兩個db中都寫入相關(guān)數(shù)據(jù)


          tinyid的原理

          Id生成系統(tǒng)要點

          在簡單系統(tǒng)中,我們常常使用db的id自增方式來標(biāo)識和保存數(shù)據(jù),隨著系統(tǒng)的復(fù)雜,數(shù)據(jù)的增多,分庫分表成為了常見的方案,db自增已無法滿足要求。
          這時候全局唯一的id生成系統(tǒng)就派上了用場。當(dāng)然這只是id生成其中的一種應(yīng)用場景。
          那么id生成系統(tǒng)有哪些要求呢?
          1. 全局唯一的id:無論怎樣都不能重復(fù),這是最基本的要求了
          2. 高性能:基礎(chǔ)服務(wù)盡可能耗時少,如果能夠本地生成最好
          3. 高可用:雖說很難實現(xiàn)100%的可用性,但是也要無限接近于100%的可用性
          4. 簡單易用: 能夠拿來即用,接入方便,同時在系統(tǒng)設(shè)計和實現(xiàn)上要盡可能的簡單

          Tinyid的實現(xiàn)原理

          我們先來看一下最常見的id生成方式,db的auto_increment,相信大家都非常熟悉,我也見過一些同學(xué)在實戰(zhàn)中使用這種方案來獲取一個id,這個方案的優(yōu)點是簡單,缺點是每次只能向db獲取一個id,性能比較差,對db訪問比較頻繁,db的壓力會比較大。
          那么是不是可以對這種方案優(yōu)化一下呢,可否一次向db獲取一批id呢?答案當(dāng)然是可以的。?
          一批id,我們可以看成是一個id范圍,例如(1000,2000],這個1000到2000也可以稱為一個"號段",我們一次向db申請一個號段,加載到內(nèi)存中,然后采用自增的方式來生成id,這個號段用完后,再次向db申請一個新的號段,這樣對db的壓力就減輕了很多,同時內(nèi)存中直接生成id,性能則提高了很多。
          那么保存db號段的表該怎設(shè)計呢?

          DB號段算法描述

          如上表,我們很容易想到的是db直接存儲一個范圍(start_id,end_id],當(dāng)這批id使用完畢后,我們做一次update操作,update start_id=2000(end_id), end_id=3000(end_id+1000),update成功了,則說明獲取到了下一個id范圍。仔細(xì)想想,實際上start_id并沒有起什么作用,新的號段總是(end_id,end_id+1000]。
          所以這里我們更改一下,db設(shè)計應(yīng)該是這樣的:

          • 這里我們增加了biz_type,這個代表業(yè)務(wù)類型,不同的業(yè)務(wù)的id隔離


          • max_id則是上面的end_id了,代表當(dāng)前最大的可用id


          • step代表號段的長度,可以根據(jù)每個業(yè)務(wù)的qps來設(shè)置一個合理的長度


          • version是一個樂觀鎖,每次更新都加上version,能夠保證并發(fā)更新的正確性 ?那么我們可以通過如下幾個步驟來獲取一個可用的號段,


          • A.查詢當(dāng)前的max_id信息:select id, biz_type, max_id, step, version from tiny_id_info where biz_type='test';


          • B.計算新的max_id: new_max_id = max_id + step


          • C.更新DB中的max_id:update tiny_id_info set max_id=#{new_max_id} , verison=version+1 where id=#{id} and max_id=#{max_id} and version=#{version}


          • D.如果更新成功,則可用號段獲取成功,新的可用號段為(max_id, new_max_id]


          • E.如果更新失敗,則號段可能被其他線程獲取,回到步驟A,進(jìn)行重試

          號段生成方案的簡單架構(gòu)

          如上我們已經(jīng)完成了號段生成邏輯,那么我們的id生成服務(wù)架構(gòu)可能是這樣的
          id生成系統(tǒng)向外提供http服務(wù),請求經(jīng)過我們的負(fù)載均衡router,到達(dá)其中一臺tinyid-server,從事先加載好的號段中獲取一個id,如果號段還沒有加載,或者已經(jīng)用完,則向db再申請一個新的可用號段,多臺server之間因為號段生成算法的原子性,而保證每臺server上的可用號段不重,從而使id生成不重。?
          可以看到如果tinyid-server如果重啟了,那么號段就作廢了,會浪費一部分id;同時id也不會連續(xù);每次請求可能會打到不同的機(jī)器上,id也不是單調(diào)遞增的,而是趨勢遞增的,不過這對于大部分業(yè)務(wù)都是可接受的。

          簡單架構(gòu)的問題

          到此一個簡單的id生成系統(tǒng)就完成了,那么是否還存在問題呢?回想一下我們最開始的id生成系統(tǒng)要求,高性能、高可用、簡單易用,在上面這套架構(gòu)里,至少還存在以下問題:
          • 當(dāng)id用完時需要訪問db加載新的號段,db更新也可能存在version沖突,此時id生成耗時明顯增加


          • db是一個單點,雖然db可以建設(shè)主從等高可用架構(gòu),但始終是一個單點


          • 使用http方式獲取一個id,存在網(wǎng)絡(luò)開銷,性能和可用性都不太好

          優(yōu)化辦法如下:

          (1)雙號段緩存
          對于號段用完需要訪問db,我們很容易想到在號段用到一定程度的時候,就去異步加載下一個號段,保證內(nèi)存中始終有可用號段,則可避免性能波動。
          (2)增加多db支持
          db只有一個master時,如果db不可用(down掉或者主從延遲比較大),則獲取號段不可用。實際上我們可以支持多個db,比如2個db,A和B,我們獲取號段可以隨機(jī)從其中一臺上獲取。
          那么如果A,B都獲取到了同一號段,我們怎么保證生成的id不重呢?
          tinyid是這么做的,讓A只生成偶數(shù)id,B只生產(chǎn)奇數(shù)id,對應(yīng)的db設(shè)計增加了兩個字段,如下所示

          delta代表id每次的增量,remainder代表余數(shù),例如可以將A,B都delta都設(shè)置2,remainder分別設(shè)置為0,1則,A的號段只生成偶數(shù)號段,B是奇數(shù)號段。
          通過delta和remainder兩個字段我們可以根據(jù)使用方的需求靈活設(shè)計db個數(shù),同時也可以為使用方提供只生產(chǎn)類似奇數(shù)的id序列。
          (3) 增加tinyid-client
          使用http獲取一個id,存在網(wǎng)絡(luò)開銷,是否可以本地生成id?為此我們提供了tinyid-client,我們可以向tinyid-server發(fā)送請求來獲取可用號段,之后在本地構(gòu)建雙號段、id生成,如此id生成則變成純本地操作,性能大大提升,因為本地有雙號段緩存,則可以容忍tinyid-server一段時間的down掉,可用性也有了比較大的提升。
          (4) tinyid最終架構(gòu)
          最終我們的架構(gòu)可能是這樣的
          • tinyid提供http和tinyid-client兩種方式接入
          • tinyid-server內(nèi)部緩存兩個號段
          • 號段基于db生成,具有原子性
          • db支持多個
          • tinyid-server內(nèi)置easy-router選擇db

          項目地址

          github地址:https://github.com/didi/tinyid

          推薦閱讀:

          成都又一程序員跳樓!一首IT版的《成都》,聽哭了所有IT人……

          面試官問:講講IP地址的分配原理?

          5T技術(shù)資源大放送!包括但不限于:C/C++,Linux,Python,Java,PHP,人工智能,單片機(jī),樹莓派,等等。在公眾號內(nèi)回復(fù)「2048」,即可免費獲?。。?/span>

          微信掃描二維碼,關(guān)注我的公眾號

          寫留言

          朕已閱?

          瀏覽 54
          點贊
          評論
          收藏
          分享

          手機(jī)掃一掃分享

          分享
          舉報
          評論
          圖片
          表情
          推薦
          點贊
          評論
          收藏
          分享

          手機(jī)掃一掃分享

          分享
          舉報
          <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>
                  三级网站看 | 亚洲操| 天天逼网| 日韩videos | 爱爱视频在线看 |