Generating good unique ids in Go
part of Go Cookbook
Imagine you’re writing a note taking application.
Each note needs a unique id.
Generating unique ids is easy if you can coordinate.
The simplest way is to get database to do it: use AUTOINCREMENT column and the database will generate unique id when you insert a new note row into a table.
What if you can’t coordinate?
For example, you want the app to also generate unique note id when offline, when it cannot talk to the database.
The requirement of non-coordinated generation of unique ids comes up often in distributed systems.
A simple solution is to generate a random id. If you give it 16 bytes of randomness, the chances of generating the same random number are non-existent.
It’s such a common problem that over 30 years ago we created a standard for this called UUID/GUID.
We can do better than GUID. A good random unique id:
There are few Go implementation of such id, following the same basic idea:
Here are Go packages for generating unique id and how their ids look like in string format:
RendeRenderCollectionViewrCode NYI
You can see how the values change over time by refreshing this test page a couple of times.
How to generate unique ids using different libraries:
import (
	"fmt"
	"log"
	"math/rand"
	"time"

	"github.com/chilts/sid"
	guuid "github.com/google/uuid"
	"github.com/kjk/betterguid"
	"github.com/lithammer/shortuuid"
	"github.com/oklog/ulid"
	"github.com/rs/xid"
	suuid "github.com/satori/go.uuid"
	"github.com/segmentio/ksuid"
	"github.com/sony/sonyflake"
)

func genShortUUID() {
	id := shortuuid.New()
	fmt.Printf("github.com/lithammer/shortuuid: %s\n", id)
}

func genUUID() {
	id := guuid.New()
	fmt.Printf("github.com/google/uuid:         %s\n", id.String())
}

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{})
	if flake == nil {
		fmt.Printf("Couldn't generate sonyflake.NewSonyflake. Doesn't work on Go Playground due to fake time.\n")
		return
	}
	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 := suuid.NewV4()
	fmt.Printf("github.com/satori/go.uuid:      %s\n", id)
}

func main() {
	genXid()
	genKsuid()
	genBetterGUID()
	genUlid()
	genSonyflake()
	genSid()
	genShortUUID()
	genUUIDv4()
	genUUID()
}
Try it online
Full code: generate-unique-id/main.go, try it online.

Which one to use?

All of them are good.
I would pick either rs/xid or segmentio/ksuid.
oklog/ulid allows custom entropy (randomness) source but pays for that with complex API.
sony/sonyflake is the smallest but also the least random. It’s based on Twitter’s design for generating IDs for tweets.
For simplicity the example code serializes sony/snoflake in base16. It would be even shorter in base62 encoding used by other libraries, but other libraries provide that out-of-the-box and for sony/snoflake I would have to implement it myself.
The last one is UUID v4 from RFC 4112, for comparison.
To learn more:
Code for this chapter: https://github.com/kjk/the-code/tree/master/go/generate-unique-id
go
Jul 9 2017

Feedback about page:

Feedback:
Optional: your email if you want me to get back to you: