Skip to content
Go back

对比 ID 生成器

Updated:
Edit

本文以 Go 为例对比当前热门的 id 生成器。由于特性与 Snowflake 相同,美团 Leaf、百度 UidGenerator、滴滴 TinyId 未列出。


对比项目

  1. 二进制位长:ID原始二进制长度(位),影响全局唯一性的概率空间。
  2. 文本长度:编码后字符串长度,影响存储与传输效率。
  3. 有序性(K-Sortable):是否按生成时间排序,对数据库索引友好性至关重要。
  4. 安全性:是否使用加密安全的随机源(如Crypto API),避免预测风险。
  5. 时间嵌入:是否包含时间戳,支持按时间范围查询。
  6. 分布式友好:是否依赖中心节点或机器ID分配,影响水平扩展复杂度。
  7. 主要优点:核心优势场景。
  8. 主要缺点:关键限制或风险。

对比表格

方案二进制位长文本长度有序性安全性时间嵌入分布式友好主要优点主要缺点
Snowflake6419数字严格递增❌(需机器ID)高性能、严格有序、空间效率高依赖时钟同步,机器ID管理复杂
Sonyflake6419数字严格递增❌(需机器ID)改进时钟回拨处理生态支持较少
UUIDv412836字符无序全局唯一、无协调存储开销大,索引效率低
UUIDv712836字符趋势递增时间有序、IETF标准文本较长
ShortUUID12822字符无序压缩UUID,减少存储仍无序,兼容性风险
NanoID12621字符无序极短、生成快(比UUID快60%)无序,不适合时序场景
ULID12826字符趋势递增可排序、Base32无混淆字符毫秒精度,高并发可能碰撞
KSUID16027字符趋势递增时间精度高(秒级)、Base62紧凑位长较大
XID9624字符趋势递增MongoDB原生支持,生成快随机性弱于UUID
Sqids可变<10字符无序极短、人类可读非全局唯一,需上下文

注:

  1. 有序性
    • 严格递增:同毫秒内通过序列号保序(如Snowflake)。
    • 趋势递增:仅时间戳部分有序,同毫秒内随机(如UUIDv7)。
  2. 安全性:高 = 使用Crypto API;中 = 混合随机源;低 = Math.random()
  3. 分布式友好:✅ = 无需中心协调;❌ = 需机器ID或时钟同步。
  4. 文本长度:基于默认编码(如UUIDv4为Hex,NanoID为Base64)。

总结

测试代码

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()
	uuidTest()
	shortuuidTest()
	nanoidTest()
	ulidTest()
	xidTest()
	ksuidTest()
}

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 uuidTest() {
	id, _ := uuid.NewV4()
	fmt.Println("uuid:", 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)
}

Edit
Share this post on:

Previous Post
Web 游戏引擎选型
Next Post
VS Code 配置