go 生成良好的随机和唯一的id

译 原文:blog.kowalczyk.info/article/JyR...

想像一下你正在开发一个记事本App。

每一条记事都需要一个唯一ID。

如果你能协调,生成唯一ID是一件非常简单的事。

最简单的方式就是通过使用数据库:使用AUTOINCREMENT属性的列,然后当你插入一条新的记事的时候,数据库将会生成一个唯一ID。

但假如你不能协调呢?

列如,你想要你的App离线的时候也能生成唯一ID,这时候它是无法连接上数据库的。

在无法协调的情况下生成唯一ID的请求通常来自于分布式系统。

一个简单的解决方案是生成一个随机ID。

假如你使用16字节长度的随机串,将不存在产生一样的随机串的几率。

这是一个常见的问题,30多年前我们为此创建了一个名为UUID / GUID的标准。

我们可以比GUID做的更好,一个好的随机唯一ID遵循以下原则:

1、唯一性:基本原则,必须满足。

2、可排序的:可以使用随机唯一ID字符串进行排序。

3、具有时间属性:同一时间内,生成的ID彼此相近。

4、随机唯一ID字符串无需转义就要以做为URL的一部份。

5、越短越好。

使用Go实现的类似代码不多,它们遵循以下规则:

1、使用时间做为ID的一部份,使ID具有时间属性。

2、使用随机数据填充剩余的部份。

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

你可以刷新测试页面看看不同时间ID格式的变化。

使用不同的包生成唯一ID的实例代码:

**

scss 复制代码
package main

// https://blog.kowalczyk.info/article/JyRZ/generating-good-unique-ids-in-go.html
// To run:
// go run main.go

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()
}

完整的实例代码请查看:the-code/go/generate-unique-id/main.go at master · kjk/the-code · GitHub

我该使用哪一个包?

上面所列的所有包都很不错。

但个人更喜欢rs/xidsegmentio/ksuid这两个包。
oklog / ulid允许使用自定义熵(随机)源,但需要使用复杂的API。
sony / sonyflake是最小的但也是最随机的。 它基于Twitter的设计,用于为推文生成ID。

为简单起见,示例代码序列化了base16 中的sony / snoflake。 在其他库使用的base62编码中它会更短,但是其他库提供了开箱即用的功能,对于sony / snoflake,我必须自己实现它。

最后一个是来自RFC 4112的UUID v4,用于比较。

更多相关知识:

相关推荐
木卫二号Coding17 分钟前
docker-开源nocodb,使用已有数据库
数据库·docker·开源
斑驳竹影23 分钟前
kafka的配置
分布式·kafka
StarRocks_labs27 分钟前
StarRocks 存算分离在得物的降本增效实践
数据库·数据仓库·湖仓
敲代码敲到头发茂密1 小时前
基于 LangChain 实现数据库问答机器人
数据库·人工智能·语言模型·langchain·机器人
一入程序无退路2 小时前
c语言传参数路径太长,导致无法获取参数
linux·c语言·数据库
沙滩de流沙2 小时前
Hadoop生态
大数据·hadoop·分布式
陌夏微秋2 小时前
STM32单片机芯片与内部47 STM32 CAN内部架构 介绍
数据库·stm32·单片机·嵌入式硬件·架构·信息与通信
计算机学无涯3 小时前
Spring事务回滚
数据库·sql·spring
web130933203983 小时前
flume对kafka中数据的导入导出、datax对mysql数据库数据的抽取
数据库·kafka·flume
张声录13 小时前
【ETCD】【实操篇(二十)】浅谈etcd集群管理的艺术:从两阶段配置到灾难恢复的设计原则
数据库·etcd