推荐使用 github.com/google/uuid 生成 UUID、github.com/oklog/ulid 生成 ULID,因其规范合规、稳定轻量;手拼时间戳+随机数易违反 RFC 4122 或 ULID 规范,导致数据库拒收、序列化失败等问题。

直接用 github.com/google/uuid 生成 UUID,用 github.com/oklog/ulid 生成 ULID —— 这是当前(2026年)生产环境最稳、最轻量、兼容性最好的组合。手写位操作或拼接时间+随机数极易出错,且不满足 RFC 4122 或 ULID 规范。
为什么不能自己拼接时间戳和随机数生成 UUID
UUID 不是“随便凑够 16 字节就行”的字符串。v4 必须在第 7 字节(索引 6)置版本位为 0100,第 9 字节(索引 8)置变体位为 10xx;漏掉任一掩码操作(比如 u[6] = (u[6] | 0x40) & 0x4F),生成的就不是合法 UUID —— 数据库可能拒收、下游服务校验失败、gRPC 序列化报错。
- 常见错误现象:
invalid UUID format、sql: converting argument $1 type: unsupported type []byte, a slice of uint8(因字节未按规范填充) - 使用场景:ORM 插入、API 请求 ID、Kafka 消息 key —— 所有依赖标准解析逻辑的地方
- 性能影响:手动生成看似快,但错误导致重试、日志爆炸、调试耗时,远超库调用开销
如何用 google/uuid 安全生成 v1/v4/v5
github.com/google/uuid 是当前事实标准,Kubernetes、Docker、etcd 全部在用,v1/v4/v5 均支持,且默认 uuid.New() 就是安全的 v4。
-
uuid.New()→ v4(推荐用于大多数场景,如用户 ID、请求 ID) -
uuid.NewUUID()→ v1(需系统有稳定 MAC 地址;若容器无网卡,会 fallback 到随机 node ID,失去时间有序性) -
uuid.NewV5(ns, name)→ 确定性 ID(适合从邮箱、URL 等输入生成固定 ID,如去重缓存 key) - 注意:
uuid.Must(uuid.NewUUID())在 v1 初始化失败时 panic,生产环境慎用;建议显式检查 error
如何用 oklog/ulid 替代 UUID 获取时间有序短 ID
ULID(Universally Unique Lexicographically Sortable Identifier)是 UUID 的替代方案:128 位、时间前缀、ASCII 编码、天然按时间排序。它比 UUID 短(26 字符 vs 36),且无需连字符。
立即学习“go语言免费学习笔记(深入)”;
- 安装:
go get github.com/oklog/ulid - 生成:
id := ulid.MustNew(ulid.Now(), ulid.DefaultEntropy)→ 输出类似01HQJ2ZQXG4T7WYVJXZQXG4T7W - 关键点:ULID 时间精度是毫秒级,同一毫秒内靠熵源(
ulid.DefaultEntropy)保证唯一;高并发下建议复用 entropy 实例,避免反复打开/dev/urandom - 兼容性:不兼容 UUID 标准,不能直接塞进期望
uuid.UUID类型的字段;需确认 ORM、DB 驱动、API schema 是否接受 string 类型 ID
UUID 和 ULID 混用时最容易被忽略的细节
两者语义不同,不能简单替换。UUID 强调“全局唯一”,ULID 强调“时间局部有序 + 唯一”。例如:
- 订单号用 ULID:方便按创建时间范围扫描,但要注意时钟回拨会导致重复(
ulid.MustNew()不校验时间单调性) - 用户 session ID 用 UUID v4:避免暴露创建时间,更符合隐私要求
- 数据库主键若用 ULID,InnoDB 聚簇索引效率优于 UUID v4,但迁移存量 UUID 数据时,字段类型必须从
CHAR(36)改为CHAR(26),且所有查询、索引、外键约束都要同步调整


















