以 Go 为例对比当前常用的 ID 生成方案。与 Snowflake 同类的 Leaf、UidGenerator、TinyId 等未单独列出。
字段说明
| 维度 | 含义 |
|---|---|
| 二进制位长 | 原始位数,决定唯一空间 |
| 文本长度 | 编码后字符串长度,影响存储与索引 |
| 有序性 | 是否时间可排序,影响 DB 索引局部性 |
| 安全性 | 是否密码学安全随机,能否被预测 |
| 时间嵌入 | 是否含时间戳,便于按时间范围查 |
| 分布式友好 | 是否无需中心节点 / 机器 ID 分配 |
有序性细分:严格递增(同毫秒内靠序列号,如 Snowflake);趋势递增(时间部分有序,同毫秒内随机,如 UUIDv7)。
方案对比
| 方案 | 二进制位长 | 文本长度 | 有序性 | 安全性 | 时间嵌入 | 分布式友好 | 主要优点 | 主要缺点 |
|---|---|---|---|---|---|---|---|---|
| Snowflake | 64 | 19 数字 | 严格递增 | 高 | 是 | 否(需机器 ID) | 高性能、索引友好 | 时钟同步、机器 ID 管理 |
| Sonyflake | 64 | 19 数字 | 严格递增 | 高 | 是 | 否(需机器 ID) | 改进回拨处理 | 生态小于 Snowflake |
| UUIDv4 | 128 | 36 字符 | 无序 | 高 | 否 | 是 | 无协调、标准 | 存储大、索引差 |
| UUIDv7 | 128 | 36 字符 | 趋势递增 | 高 | 是 | 是 | IETF 标准、时间有序 | 文本仍较长 |
| ShortUUID | 128 | 22 字符 | 无序 | 高 | 否 | 是 | 压缩 UUID | 仍无序 |
| NanoID | 126 | 21 字符 | 无序 | 高 | 否 | 是 | 短、生成快 | 无序,不适合时序 |
| ULID | 128 | 26 字符 | 趋势递增 | 高 | 是 | 是 | Base32、可排序 | 毫秒精度,极高并发需注意 |
| KSUID | 160 | 27 字符 | 趋势递增 | 高 | 是 | 是 | 秒级时间 + Base62 | 位长偏大 |
| XID | 96 | 24 字符 | 趋势递增 | 中 | 是 | 是 | MongoDB 生态、快 | 随机性弱于 UUID |
| Sqids | 可变 | <10 字符 | 无序 | 低 | 否 | 是 | 极短、人类可读 | 非全局唯一,需上下文 |
安全性:高 = Crypto API;中 = 混合随机源;低 = Math.random() 类。
推荐
- 要严格时序 + 数字 ID、能接受运维机器号:Snowflake / Sonyflake。
- 无中心协调、要标准与时间有序:UUIDv7(新系统默认倾向)或 ULID / KSUID。
- URL、短链、前端展示:NanoID 或 ShortUUID。
- 短码、可逆、非全局唯一(订单号展示等):Sqids(别当主键唯一方案)。
测试代码
package main
import (
"fmt"
"math/rand"
"strconv"
"time"
"github.com/bwmarrin/snowflake"
"github.com/gofrs/uuid"
"github.com/jaevor/go-nanoid"
"github.com/lithammer/shortuuid/v4"
"github.com/oklog/ulid"
"github.com/rs/xid"
"github.com/segmentio/ksuid"
"github.com/sony/sonyflake"
"github.com/sqids/sqids-go"
)
func main() {
snowflakeTest()
sonyflakeTest()
uuidV4Test()
uuidV7Test()
shortuuidTest()
nanoidTest()
ulidTest()
xidTest()
ksuidTest()
sqidsTest()
}
func snowflakeTest() {
n, _ := snowflake.NewNode(1)
id := n.Generate().String()
fmt.Println("snowflake:", id, "length:", len(id))
}
func sonyflakeTest() {
t := time.Now()
s := sonyflake.NewSonyflake(sonyflake.Settings{
StartTime: t,
MachineID: func() (uint16, error) { return 1, nil },
CheckMachineID: func(u uint16) bool { return true },
})
id, _ := s.NextID()
fmt.Println("sonyflake:", id, "length:", len(strconv.FormatUint(id, 10)))
}
func uuidV4Test() {
id, _ := uuid.NewV4()
fmt.Println("uuid v4:", id.String(), "length:", len(id.String()))
}
func uuidV7Test() {
// 需 github.com/gofrs/uuid v5.4+
id, _ := uuid.NewV7()
fmt.Println("uuid v7:", id.String(), "length:", len(id.String()))
}
func shortuuidTest() {
id := shortuuid.New()
fmt.Println("shortUUID:", id, "length:", len(id))
a := "12345#$%^&*67890qwerty/;'~!@uiopasdfghjklzxcvbnm,.()_+·><"
id = shortuuid.NewWithAlphabet(a)
fmt.Println("shortUuid2:", id, "length:", len(id))
}
func nanoidTest() {
s, _ := nanoid.Standard(21)
id := s()
fmt.Println("nanoid:", id, "length:", len(id))
c, _ := nanoid.CustomASCII("0123456789", 12)
id = c()
fmt.Println("nanoid2:", id, "length:", len(id))
}
func ulidTest() {
t := time.Now().UTC()
e := rand.New(rand.NewSource(t.UnixNano()))
id := ulid.MustNew(ulid.Timestamp(t), e)
fmt.Println("ulid:", id.String(), "length:", len(id.String()))
}
func xidTest() {
id := xid.New()
fmt.Println("xid:", id, "length:", len(id))
}
func ksuidTest() {
id := ksuid.New()
fmt.Println("ksuid:", id, "length:", len(id))
}
func sqidsTest() {
s, _ := sqids.New()
id, _ := s.Encode([]uint64{1, 2, 3})
fmt.Println("sqids:", id, "length:", len(id))
s, _ = sqids.New(sqids.Options{
MinLength: 10,
})
id, _ = s.Encode([]uint64{1, 2, 3})
fmt.Println("sqids2:", id, "length:", len(id))
numbers := s.Decode(id)
fmt.Println("sqids numbers:", numbers)
}