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,用于比较。

更多相关知识:

相关推荐
Ai 编码助手1 小时前
MySQL中distinct与group by之间的性能进行比较
数据库·mysql
陈燚_重生之又为程序员1 小时前
基于梧桐数据库的实时数据分析解决方案
数据库·数据挖掘·数据分析
caridle1 小时前
教程:使用 InterBase Express 访问数据库(五):TIBTransaction
java·数据库·express
白云如幻1 小时前
MySQL排序查询
数据库·mysql
萧鼎1 小时前
Python并发编程库:Asyncio的异步编程实战
开发语言·数据库·python·异步
^velpro^1 小时前
数据库连接池的创建
java·开发语言·数据库
荒川之神1 小时前
ORACLE _11G_R2_ASM 常用命令
数据库·oracle
IT培训中心-竺老师2 小时前
Oracle 23AI创建示例库
数据库·oracle
小白学大数据2 小时前
JavaScript重定向对网络爬虫的影响及处理
开发语言·javascript·数据库·爬虫
time never ceases2 小时前
使用docker方式进行Oracle数据库的物理迁移(helowin/oracle_11g)
数据库·docker·oracle