<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>

          10分鐘搞定!Golang分布式ID集合

          共 11284字,需瀏覽 23分鐘

           ·

          2022-11-08 17:03

          導(dǎo)語(yǔ)?|? 本文是基于最近對(duì)Golang分布式ID的相關(guān)討論,希望本文內(nèi)容可以對(duì)相關(guān)技術(shù)感興趣的開發(fā)者提供一點(diǎn)經(jīng)驗(yàn)和幫助。


          一、本地ID生成器


          (一) uuid


          uuid有兩種包:


          • github.com/google/uuid ,僅支持V1和V4版本。


          • github.com/gofrs/uuid ,支持全部五個(gè)版本。


          下面簡(jiǎn)單說(shuō)下五種版本的區(qū)別:


          • Version 1,基于mac地址、時(shí)間戳。


          • Version 2,based on timestamp,MAC address and POSIX UID/GID (DCE 1.1)


          • Version 3,Hash獲取入?yún)⒉?duì)結(jié)果進(jìn)行MD5。


          • Version 4,純隨機(jī)數(shù)。


          • Version 5,based on SHA-1 hashing of a named value。


          特點(diǎn)


          • 5個(gè)版本可供選擇。


          • 定長(zhǎng)36字節(jié),偏長(zhǎng)。


          • 無(wú)序。


                
                  package mian
                
                
                  
                    
          import ( "github.com/gofrs/uuid" "fmt" )
          func main() { // Version 1:時(shí)間+Mac地址 id, err := uuid.NewV1() if err != nil { fmt.Printf("uuid NewUUID err:%+v", err) } // id: f0629b9a-0cee-11ed-8d44-784f435f60a4 length: 36 fmt.Println("id:", id.String(), "length:", len(id.String()))
          // Version 4:是純隨機(jī)數(shù),error會(huì)在內(nèi)部報(bào)panic id, err = uuid.NewV4() if err != nil { fmt.Printf("uuid NewUUID err:%+v", err) } // id: 3b4d1268-9150-447c-a0b7-bbf8c271f6a7 length: 36 fmt.Println("id:", id.String(), "length:", len(id.String())) }



          (二)shortuuid


          初始值基于uuid Version4;第二步根據(jù)alphabet變量長(zhǎng)度(定長(zhǎng)57)計(jì)算id長(zhǎng)度(定長(zhǎng)22);第三步依次用DivMod(歐幾里得除法和模)返回值與alphabet做映射,合并生成id。


          特點(diǎn)


          • 基于uuid,但比uuid的長(zhǎng)度短,定長(zhǎng)22字節(jié)。


                
                  package mian
                
                
                  
                    
          import ( "github.com/lithammer/shortuuid/v4" "fmt" )
          func main() { id := shortuuid.New() // id: iDeUtXY5JymyMSGXqsqLYX length: 22 fmt.Println("id:", id, "length:", len(id))
          // V22s2vag9bQEZCWcyv5SzL 固定不變 id = shortuuid.NewWithNamespace("http://127.0.0.1.com") // id: K7pnGHAp7WLKUSducPeCXq length: 22 fmt.Println("id:", id, "length:", len(id)) // NewWithAlphabet函數(shù)可以用于自定義的基礎(chǔ)字符串,字符串要求不重復(fù)、定長(zhǎng)57 str := "12345#$%^&*67890qwerty/;'~!@uiopasdfghjklzxcvbnm,.()_+·><" id = shortuuid.NewWithAlphabet(str) // id: q7!o_+y('@;_&dyhk_in9/ length: 22 fmt.Println("id:", id, "length:", len(id)) }



          (三)xid


          xid是由時(shí)間戳、進(jìn)程id、Mac地址、隨機(jī)數(shù)組成。有序性來(lái)源于對(duì)隨機(jī)數(shù)部分的原子+1。


          b4b31d51afe2ab26180a39ab35a0239f.webp

          特點(diǎn)


          • 長(zhǎng)度短。


          • 有序。


          • 不重復(fù)。


          • 時(shí)間戳這個(gè)隨機(jī)數(shù)原子+1操作,避免了時(shí)鐘回?fù)艿膯栴}。


          下面的代碼根據(jù)需求進(jìn)行了魔改。


                
                  package mian
                
                
                  
                    
          import ( "github.com/rs/xid" "fmt" )
          func main() { // hostname+pid+atomic.AddUint32 id := xid.New() containerName := "test" // 由于xid默認(rèn)使用可重復(fù)ip地址填充4 5 6位。 ????//?實(shí)際場(chǎng)景中,服務(wù)都是部署在docker中,這里把ip地址位替換成了容器名 ????//?這里只取了容器名MD5的前3位,驗(yàn)證會(huì)重復(fù),放棄使用 containerNameID := make([]byte, 3) hw := md5.New() hw.Write([]byte(containerName)) copy(containerNameID, hw.Sum(nil)) id[4] = containerNameID[0] id[5] = containerNameID[1] id[6] = containerNameID[2] // id: cbgjhf89htlrr1955d5g length: 12 fmt.Println("id:", id, "length:", len(id)) }



          (四)ksuid


          由隨機(jī)數(shù)和時(shí)間戳組成。時(shí)間戳占前4字節(jié),后面均為隨機(jī)數(shù):


                
                  package mian
                
                
                  
                    
          import ( "github.com/segmentio/ksuid" "fmt" )
          func main() {
          id := ksuid.New() // id: 2CWvPg766SUvezbiiV9nzrTZsgf length: 20 fmt.Println("id:", id, "length:", len(id)) id1 := ksuid.New() id2 := ksuid.New() // id1:2CTqTLRxCh48y7oUQzQHrgONT2k id2:2CTqTHf07C09CXyRMHdGKXnY5HP fmt.Println(id1, id2)
          // 支持ID對(duì)比,這個(gè)功能比較雞肋了,目前沒想到有用的地方 compareResult := ksuid.Compare(id1, id2) fmt.Println(compareResult) // 1
          // 判斷順序性 isSorted := ksuid.IsSorted([]ksuid.KSUID{id2, id1}) fmt.Println(isSorted) // true 降序 }



          (五)ulid


          隨機(jī)數(shù)和時(shí)間戳組成


                
                  package mian
                
                
                  
                    
          import ( "github.com/oklog/ulid" "fmt" )
          func main() { t := time.Now().UTC() entropy := rand.New(rand.NewSource(t.UnixNano())) id := ulid.MustNew(ulid.Timestamp(t), entropy) // id: 01G902ZSM96WV5D5DC5WFHF8WY length: 26 fmt.Println("id:", id.String(), "length:", len(id.String())) }



          (六)snowflake


          大名鼎鼎的雪花算法,這里不做過(guò)多介紹了。相對(duì)于UUID來(lái)說(shuō),雪花算法不會(huì)暴露MAC地址更安全、生成的ID也不會(huì)過(guò)于冗余。雪花的一部分ID序列是基于時(shí)間戳的,那么時(shí)鐘回?fù)艿膯栴}就來(lái)了。上面提到的xid,一定程度上避時(shí)鐘回?fù)艿挠绊憽D敲词裁词菚r(shí)鐘回?fù)埽竺鏁?huì)提到。


                
                  package main
                
                
                  
                    
          import( "fmt" "github.com/bwmarrin/snowflake" )
          func main() { node, err := snowflake.NewNode(1) if err != nil { fmt.Println(err) return }
          id := node.Generate().String() // id: 1552614118060462080 length: 19 fmt.Println("id:", id, "length:", len(id)) }



          二、數(shù)據(jù)庫(kù)自增ID


          這里常規(guī)是指數(shù)據(jù)庫(kù)主鍵自增索引。特點(diǎn)如下:


          • 架構(gòu)簡(jiǎn)單容易實(shí)現(xiàn)。


          • ID有序遞增,IO寫入連續(xù)性好。


          • INT和BIGINT類型占用空間較小。


          • 由于有序遞增,易暴露業(yè)務(wù)量。


          • 受到數(shù)據(jù)庫(kù)性能限制,對(duì)高并發(fā)場(chǎng)景不友好。


          • bigint最大是2^64-1,但是數(shù)據(jù)庫(kù)單表肯定放不了這么多,那么就涉及到分表。 如果業(yè)務(wù)量真的太大了,主鍵的自增id漲到頭了,會(huì)發(fā)生什么? 報(bào)錯(cuò): 鍵沖突。



          三、Redis生成ID


          通過(guò)redis的原子操作INCR和INCRBY獲得id。相比數(shù)據(jù)庫(kù)自增ID,redis性能更好、更加靈活。不過(guò)架構(gòu)強(qiáng)依賴redis,redis在整個(gè)架構(gòu)中會(huì)產(chǎn)生單點(diǎn)問題。在流量較大的場(chǎng)景下,網(wǎng)絡(luò)耗時(shí)也可能成為瓶頸。



          四、ZooKeeper唯一ID


          ZooKeeper是使用了Znode結(jié)構(gòu)中的Zxid實(shí)現(xiàn)順序增ID。Zookeeper類似一個(gè)文件系統(tǒng),每個(gè)節(jié)點(diǎn)都有唯一路徑名(Znode),Zxid是個(gè)全局事務(wù)計(jì)數(shù)器,每個(gè)節(jié)點(diǎn)發(fā)生變化都會(huì)記錄響應(yīng)的版本(Zxid),這個(gè)版本號(hào)是全局唯一且順序遞增的。這種架構(gòu)還是出現(xiàn)了ZooKeeper的單點(diǎn)問題。



          五、號(hào)段模式


          (一)Leaf-segment


          把數(shù)據(jù)庫(kù)自增主鍵換成了計(jì)數(shù)法。每個(gè)業(yè)務(wù)分配一個(gè)biz_tag、并記錄各業(yè)務(wù)最大id(max_id)、號(hào)段跨度(step)等數(shù)據(jù)。這樣每次取號(hào)只需要更新biz_tag對(duì)應(yīng)的max_id,就可以拿到step個(gè)id。


          2545f5083649d39f33f645a1a4396e50.webp


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


          • 除了擁有自增ID的優(yōu)點(diǎn)之外,在性能上比自增ID更好


          • 擴(kuò)展靈活。


          • 使用靈活、可配置性強(qiáng)。


          • 緩存機(jī)制,突發(fā)狀況下短時(shí)間內(nèi)能保證服務(wù)正常運(yùn)轉(zhuǎn)。



          缺點(diǎn)


          • id是有序自增,容易暴露信息,不可用于訂單。


          • 在leaf的緩存ID用完再去獲取新號(hào)段的間隙,性能會(huì)有波動(dòng)。


          • 強(qiáng)依賴DB。



          (二)增強(qiáng)版Leaf-segment


          增強(qiáng)版是對(duì)上面描述的缺點(diǎn)2進(jìn)行的改進(jìn)——雙cache。在leaf的ID消耗到一定百分比時(shí),常駐的后臺(tái)進(jìn)程會(huì)預(yù)先去號(hào)段服務(wù)獲取新的號(hào)段并緩存。具體消耗百分比、及號(hào)段step根據(jù)業(yè)務(wù)消耗速度來(lái)定。?


          014bc1462efc527dc584b9de2cbaa9e3.webp



          (三)Tinyid


          和增強(qiáng)版Leaf-segment類似,也是號(hào)段模式,提前加載號(hào)段。?


          6269e6dd2c7ba15580a251b0f11ae374.webp



          (四)Leaf-snowflake


          時(shí)鐘回?fù)?/span>


          服務(wù)器上的時(shí)間突然倒退回之前的時(shí)間。可能是為的調(diào)整時(shí)間;也可能是服務(wù)器之間的時(shí)間校對(duì)。


          實(shí)現(xiàn)方案


          ed68b8ed18ba5c4e383205c9799f0c85.webp


          用Zookeeper順序增、全局唯一的節(jié)點(diǎn)版本號(hào),替換了原有的機(jī)器地址。解決了時(shí)鐘回?fù)艿膯栴}。前面介紹ZooKeeper的缺點(diǎn),強(qiáng)依賴ZooKeeper、大流量下的網(wǎng)絡(luò)瓶頸。下圖的方案在Leaf-snowflake 中通過(guò)緩存一個(gè)ZooKeeper文件夾,提高可用性。運(yùn)行時(shí)運(yùn)行時(shí),時(shí)差小于5ms會(huì)等待時(shí)差兩倍時(shí)間,如果時(shí)差大于5ms報(bào)警并停止啟動(dòng)。?


          96811fff45cdab670c9f149b68ddf500.webp


          ?作者簡(jiǎn)介


          faf750d7b869da6cb1bf6e13b9fc488a.webp

          陳冬

          騰訊后臺(tái)開發(fā)工程師

          騰訊后臺(tái)開發(fā)工程師,目前負(fù)責(zé)騰訊視頻后端中間件開發(fā)工作,在消息隊(duì)列和go性能優(yōu)化方面有豐富經(jīng)驗(yàn)。



          推薦閱讀


          福利
          我為大家整理了一份 從入門到進(jìn)階的Go學(xué)習(xí)資料禮包 ,包含學(xué)習(xí)建議:入門看什么,進(jìn)階看什么。 關(guān)注公眾號(hào) 「polarisxu」,回復(fù)? ebook ?獲取;還可以回復(fù)「進(jìn)群」,和數(shù)萬(wàn) Gopher 交流學(xué)習(xí)。


          瀏覽 52
          點(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>
                  亚洲无码中文字幕在线观看视频 | 黄色一级网址 | 免费中文A V在线 | 学生妹毛片wwwww | 美女操逼国产欧美亚洲色 |