[翻譯]Golang中生成良好的唯一ID的方式

想像一下你正在開(kāi)發(fā)一個(gè)記事本App。
每一條記事都需要一個(gè)唯一ID。
如果你能協(xié)調(diào),生成唯一ID是一件非常簡(jiǎn)單的事。
最簡(jiǎn)單的方式就是通過(guò)使用數(shù)據(jù)庫(kù):使用AUTOINCREMENT屬性的列,然后當(dāng)你插入一條新的記事的時(shí)候,數(shù)據(jù)庫(kù)將會(huì)生成一個(gè)唯一ID。

但假如你不能協(xié)調(diào)呢?
列如,你想要你的App離線的時(shí)候也能生成唯一ID,這時(shí)候它是無(wú)法連接上數(shù)據(jù)庫(kù)的。

在無(wú)法協(xié)調(diào)的情況下生成唯一ID的請(qǐng)求通常來(lái)自于分布式系統(tǒng)。
一個(gè)簡(jiǎn)單的解決方案是生成一個(gè)隨機(jī)ID。
假如你使用16字節(jié)長(zhǎng)度的隨機(jī)串,將不存在產(chǎn)生一樣的隨機(jī)串的幾率。
這是一個(gè)常見(jiàn)的問(wèn)題,30多年前我們?yōu)榇藙?chuàng)建了一個(gè)名為UUID / GUID的標(biāo)準(zhǔn)。

我們可以比GUID做的更好,一個(gè)好的隨機(jī)唯一ID遵循以下原則:
1、唯一性:基本原則,必須滿足。
2、可排序的:可以使用隨機(jī)唯一ID字符串進(jìn)行排序。
3、具有時(shí)間屬性:同一時(shí)間內(nèi),生成的ID彼此相近。
4、隨機(jī)唯一ID字符串無(wú)需轉(zhuǎn)義就要以做為URL的一部份。
5、越短越好。

使用Go實(shí)現(xiàn)的類似代碼不多,它們遵循以下規(guī)則:
1、使用時(shí)間做為ID的一部份,使ID具有時(shí)間屬性。
2、使用隨機(jī)數(shù)據(jù)填充剩余的部份。
3、編碼唯一ID為字符串,滿足可排序及URL安全的條件。

下面列出的是一些生成唯一ID的Go包,以及它們生成的ID字符串的格式:

Package Id Format
github.com/segmentio/ksuid 0pPKHjWprnVxGH7dEsAoXX2YQvU 4 bytes of time (seconds) + 16 random bytes
github.com/rs/xid b50vl5e54p1000fo3gh0 4 bytes of time (seconds) + 3 byte machine id + 2 byte process id + 3 bytes random
github.com/kjk/betterguid -Kmdih_fs4ZZccpx2Hl1 8 bytes of time (milliseconds) + 9 random bytes
github.com/sony/sonyflake 20f8707d6000108 ~6 bytes of time (10 ms) + 1 byte sequence + 2 bytes machine id
github.com/oklog/ulid 01BJMVNPBBZC3E36FJTGVF0C4S 6 bytes of time (milliseconds) + 8 bytes random
github.com/chilts/sid 1JADkqpWxPx-4qaWY47~FqI 8 bytes of time (ns) + 8 random bytes
github.com/satori/go.uuid 5b52d72c-82b3-4f8e-beb5-437a974842c UUIDv4 from

你可以刷新測(cè)試頁(yè)面看看不同時(shí)間ID格式的變化。
使用不同的包生成唯一ID的實(shí)例代碼:

import (
    "github.com/chilts/sid"
    "github.com/kjk/betterguid"
    "github.com/oklog/ulid"
    "github.com/rs/xid"
    "github.com/satori/go.uuid"
    "github.com/segmentio/ksuid"
    "github.com/sony/sonyflake"
)

// To run:
// go run main.go

func genXid() {
    id := xid.New()
    fmt.Printf("github.com/rs/xid:           %s\n", id.String())
}

func genKsuid() {
    id := ksuid.New()
    fmt.Printf("github.com/segmentio/ksuid:  %s\n", id.String())
}

func genBetterGUID() {
    id := betterguid.New()
    fmt.Printf("github.com/kjk/betterguid:   %s\n", id)
}

func genUlid() {
    t := time.Now().UTC()
    entropy := rand.New(rand.NewSource(t.UnixNano()))
    id := ulid.MustNew(ulid.Timestamp(t), entropy)
    fmt.Printf("github.com/oklog/ulid:       %s\n", id.String())
}

func genSonyflake() {
    flake := sonyflake.NewSonyflake(sonyflake.Settings{})
    id, err := flake.NextID()
    if err != nil {
        log.Fatalf("flake.NextID() failed with %s\n", err)
    }
    // Note: this is base16, could shorten by encoding as base62 string
    fmt.Printf("github.com/sony/sonyflake:   %x\n", id)
}

func genSid() {
    id := sid.Id()
    fmt.Printf("github.com/chilts/sid:       %s\n", id)
}

func genUUIDv4() {
    id := uuid.NewV4()
    fmt.Printf("github.com/satori/go.uuid:   %s\n", id)
}

func main() {
    genXid()
    genKsuid()
    genBetterGUID()
    genUlid()
    genSonyflake()
    genSid()
    genUUIDv4()
}

完整的實(shí)例代碼請(qǐng)查看:generate-unique-id/main.go

我該使用哪一個(gè)包?

上面所列的所有包都很不錯(cuò)。
但個(gè)人更喜歡rs/xidsegmentio/ksuid這兩個(gè)包。
oklog / ulid允許使用自定義熵(隨機(jī))源,但需要使用復(fù)雜的API。
sony / sonyflake是最小的但也是最隨機(jī)的。 它基于Twitter的設(shè)計(jì),用于為推文生成ID。
為簡(jiǎn)單起見(jiàn),示例代碼序列化了base16 中的sony / snoflake。 在其他庫(kù)使用的base62編碼中它會(huì)更短,但是其他庫(kù)提供了開(kāi)箱即用的功能,對(duì)于sony / snoflake,我必須自己實(shí)現(xiàn)它。
最后一個(gè)是來(lái)自RFC 4112的UUID v4,用于比較。

更多相關(guān)知識(shí):

本文的代碼: https://github.com/kjk/go-cookbook/tree/master/generate-unique-id
本文翻譯自:Generating good unique ids in Go

?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時(shí)請(qǐng)結(jié)合常識(shí)與多方信息審慎甄別。
平臺(tái)聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點(diǎn),簡(jiǎn)書(shū)系信息發(fā)布平臺(tái),僅提供信息存儲(chǔ)服務(wù)。

相關(guān)閱讀更多精彩內(nèi)容

  • Spring Cloud為開(kāi)發(fā)人員提供了快速構(gòu)建分布式系統(tǒng)中一些常見(jiàn)模式的工具(例如配置管理,服務(wù)發(fā)現(xiàn),斷路器,智...
    卡卡羅2017閱讀 136,506評(píng)論 19 139
  • 本文主要介紹在一個(gè)分布式系統(tǒng)中, 怎么樣生成全局唯一的 ID 一, 問(wèn)題描述 在分布式系統(tǒng)存在多個(gè) Shard 的...
    hanayona閱讀 2,139評(píng)論 0 5
  • 關(guān)于Mongodb的全面總結(jié) MongoDB的內(nèi)部構(gòu)造《MongoDB The Definitive Guide》...
    中v中閱讀 32,275評(píng)論 2 89
  • 店鋪淘客就是利用店長(zhǎng)寶把商家放在淘寶里面的商品,采集到自己的店鋪中去,自己加上差價(jià)去推廣。 舉個(gè)例子說(shuō)吧: 1、先...
    蔚藍(lán)色的天空c閱讀 478評(píng)論 0 0
  • 一河奔流,它來(lái)到陡崖邊。 崖邊一行苦楝樹(shù),還有黃楝樹(shù),就連那四圍的土,也有很深的苦味了。 河水到此,深皺眉頭。就在...
    八里山人程遠(yuǎn)河閱讀 492評(píng)論 8 8

友情鏈接更多精彩內(nèi)容