Go uuid 标准库怎样生成并解析 UUID
来源:17golang原创
时间:2026-11-13 21:46:22 180浏览 收藏
如果项目已经使用 Go 1.27,生成和解析 UUID 不必再先引入第三方包:标准库的 uuid 提供了 UUID 类型、New、NewV4、NewV7、Parse 和文本编解码方法。实际开发可以按三个判断来选 API:普通随机标识用 uuid.New(),明确要版本 4 用 uuid.NewV4(),需要时间有序特征时考虑 uuid.NewV7();来自请求、文件或数据库的字符串则用返回错误的 uuid.Parse()。
官方文档:https://pkg.go.dev/uuid
本文只讨论生成、解析、格式化和输入错误。下面的示例以 Go 1.27 标准库为前提,不把 MustParse 当成外部输入校验器。
Go 1.27 的 uuid 包解决了什么
Go 1.27 的发布说明把 uuid 列为新增标准库包,官方文档说明它遵循 RFC 9562,并使用密码学安全的随机数生成器提供随机部分。UUID 的底层表示是 [16]byte,因此可以直接比较,也适合放进 map 的键中。
这和“把 36 个字符当作普通字符串”有两个区别:一是值对象本身固定为 128 位,二是格式化和解析都由包统一处理,业务代码不用手工切分连字符。
| 需求 | 建议 API | 注意点 |
|---|---|---|
| 通常的全新标识 | uuid.New() | 当前等价于 NewV4() |
| 明确使用随机版本 4 | uuid.NewV4() | 包含 122 位随机数据 |
| 希望按时间方向排序 | uuid.NewV7() | 时钟回拨时仍需保留业务排序兜底 |
| 解析外部文本 | uuid.Parse(s) | 检查 error,不要直接 panic |
先用 New 生成一个普通 UUID
当业务并不依赖具体 UUID 版本时,直接使用 uuid.New。它返回一个值,不需要额外传入随机源,也不会把生成失败作为普通业务错误返回。
package main
import (
"fmt"
"uuid"
)
func main() {
// New 适合大多数只需要新标识的业务对象。
id := uuid.New()
// String 使用标准的小写十六进制和连字符格式。
fmt.Println(id.String())
}
输出格式类似 f81d4fae-7dec-11d0-a765-00a0c91e6bf6,但每次生成的具体值不同。这里的关键不是记住某个样例,而是把 UUID 留在领域对象内部,只有进入 JSON、日志或 URL 时再转换成文本。

需要明确版本时选 NewV4 或 NewV7
NewV4 生成随机版本 4 UUID。它适合分布式创建主键、请求 ID 或不会按创建时间排序的外部标识。NewV7 在最高位包含时间戳,同时保留随机数据,适合希望索引写入大致沿时间方向排列的场景。
package main
import (
"fmt"
"uuid"
)
func main() {
// 明确版本 4,表达“随机 UUID”的意图。
randomID := uuid.NewV4()
// 版本 7 带有时间成分,但不等于严格的业务时间。
timeOrderedID := uuid.NewV7()
fmt.Println("v4:", randomID)
fmt.Println("v7:", timeOrderedID)
}
不要把版本 7 当成全局严格递增序列。官方文档明确指出,系统时钟向后移动时,递增排序保证可能受到影响;分页或事件排序仍应使用明确的创建时间、序列号或数据库排序键。
用 Parse 接收请求参数和数据库文本
外部输入应调用 uuid.Parse,因为它返回 (UUID, error)。官方实现支持标准连字符形式、外层花括号形式、urn:uuid: 形式和没有连字符的 32 位十六进制形式,字母大小写也可以不同。
package main
import (
"fmt"
"uuid"
)
func readID(raw string) (uuid.UUID, error) {
// Parse 负责格式边界;调用方决定把错误映射成 400 还是数据修复提示。
id, err := uuid.Parse(raw)
if err != nil {
return uuid.Nil(), fmt.Errorf("解析 UUID 失败: %w", err)
}
return id, nil
}
func main() {
// 这是 Parse 支持的无连字符形式,解析成功后仍统一输出标准格式。
id, err := readID("f81d4fae7dec11d0a76500a0c91e6bf6")
if err != nil {
fmt.Println(err)
return
}
fmt.Println(id.String())
}
在 HTTP handler 中,err != nil 通常应该转换成客户端输入错误;在读取内部数据库记录时,则可以附加记录主键和来源字段后交给上层处理。不要用字符串长度判断“看起来像 UUID”,因为长度正确并不代表十六进制字符和分隔符合法。

用 String 和文本接口统一输出
String 返回小写十六进制加连字符的标准文本。如果类型要直接参与 JSON、配置文件或其他文本编码流程,可以使用 MarshalText 和 UnmarshalText 所实现的文本接口。
package main
import (
"fmt"
"uuid"
)
func main() {
var id uuid.UUID
// UnmarshalText 接受 Parse 支持的文本形式,并把错误留给调用方。
if err := id.UnmarshalText([]byte("{f81d4fae-7dec-11d0-a765-00a0c91e6bf6}")); err != nil {
fmt.Println("文本 UUID 无效:", err)
return
}
// MarshalText 与 String 使用同一种标准文本表示。
text, err := id.MarshalText()
if err != nil {
fmt.Println("格式化 UUID 失败:", err)
return
}
fmt.Println(string(text))
}
如果只是拼接日志或返回路径参数,String 最直观;如果要让结构体字段被通用编码器识别,保留文本接口通常更省事。对比 UUID 时可以直接使用 ==,或者使用 Compare 获得 -1、0、1 的排序结果。
输入错误时为什么不要滥用 MustParse
MustParse 在字符串无效时会 panic。它适合包级常量、测试夹具或启动时确定不会变化的配置;不适合 HTTP 参数、消息队列载荷、用户导入文件和数据库中可能已经损坏的数据。
package main
import (
"fmt"
"uuid"
)
func parseForRequest(raw string) (uuid.UUID, error) {
// 外部输入必须返回错误,不能因为一个参数让整个服务 panic。
id, err := uuid.Parse(raw)
if err != nil {
return uuid.Nil(), fmt.Errorf("无效的请求 ID: %w", err)
}
return id, nil
}
func fixedID() uuid.UUID {
// 只有字符串由程序员控制且错误应在启动/测试阶段暴露时才使用 MustParse。
return uuid.MustParse("f81d4fae-7dec-11d0-a765-00a0c91e6bf6")
}
还要注意 Go 版本:uuid 是 Go 1.27 新增的标准库包,旧版本项目不能只修改 import 就获得同一 API。若项目仍要支持旧版本,应在依赖方案、构建矩阵和迁移计划中明确第三方 UUID 包或自维护实现,并避免把 Go 1.27 的示例直接放进旧版本模块。
一张表记住生成与解析边界
| 场景 | 写法 | 失败策略 |
|---|---|---|
| 新建普通标识 | uuid.New() | 直接得到 UUID 值 |
| 随机版本 4 | uuid.NewV4() | 用于明确的随机语义 |
| 时间有序倾向 | uuid.NewV7() | 不能代替严格序列号 |
| 解析外部字符串 | uuid.Parse(s) | 检查 error,返回业务错误 |
| 固定常量 | uuid.MustParse(s) | 仅让程序员错误在启动/测试时暴露 |
常见问题
Go 1.26 能直接 import uuid 吗?
不能把 Go 1.27 新增的标准库 API 当作旧版本 API 使用。旧版本需要选择兼容的第三方包或保留自己的适配层,并在 go.mod 与 CI 版本矩阵中说明。
uuid.New 和 uuid.NewV4 有什么区别?
当前官方文档说明 New 等价于 NewV4。不需要表达版本时用 New 更简洁;需要在代码评审中明确随机版本时用 NewV4。
解析失败时可以返回 uuid.Nil 吗?
可以把 uuid.Nil() 作为零值返回,但必须同时保留并检查 error。只看零值会把“解析失败”和“输入确实是 Nil UUID”混在一起。
UUID 文本应该统一大写吗?
标准库的 String 使用小写十六进制和连字符形式。除非外部协议明确要求大写,否则直接使用该规范化输出可以减少比较和日志差异。
最终可以把选择压缩成一句话:生成内部 ID 用 New,需要版本语义时用 NewV4 或 NewV7,接收外部文本一律用 Parse 并检查 error,只有完全由代码控制的固定值才使用 MustParse。
-
450 收藏
-
239 收藏
-
202 收藏
-
Golang · Go教程 | 37分钟前 | 标准库 · HTTP · 性能 · 连接池 · Go教程 · Go net/http Transport Response.Body HTTP连接复用 响应体关闭339 收藏
-
453 收藏
-
Golang · Go教程 | 40分钟前 | 标准库 · pprof · Go教程 · Go Debug pprof 性能诊断 runtime/pprof goroutine profile Profile.WriteTo224 收藏
-
137 收藏
-
295 收藏
-
101 收藏
-
373 收藏
-
Golang · Go教程 | 48分钟前 | 标准库 · JSON · 错误处理 · 数据校验 · Go教程 · Go IsValid encoding/json/jsontext jsontext.Value JSON片段 WriteValue280 收藏
-
143 收藏
-
- 前端进阶之JavaScript设计模式
- 设计模式是开发人员在软件开发过程中面临一般问题时的解决方案,代表了最佳的实践。本课程的主打内容包括JS常见设计模式以及具体应用场景,打造一站式知识长龙服务,适合有JS基础的同学学习。
- 立即学习 543次学习
-
- GO语言核心编程课程
- 本课程采用真实案例,全面具体可落地,从理论到实践,一步一步将GO核心编程技术、编程思想、底层实现融会贯通,使学习者贴近时代脉搏,做IT互联网时代的弄潮儿。
- 立即学习 516次学习
-
- 简单聊聊mysql8与网络通信
- 如有问题加微信:Le-studyg;在课程中,我们将首先介绍MySQL8的新特性,包括性能优化、安全增强、新数据类型等,帮助学生快速熟悉MySQL8的最新功能。接着,我们将深入解析MySQL的网络通信机制,包括协议、连接管理、数据传输等,让
- 立即学习 500次学习
-
- JavaScript正则表达式基础与实战
- 在任何一门编程语言中,正则表达式,都是一项重要的知识,它提供了高效的字符串匹配与捕获机制,可以极大的简化程序设计。
- 立即学习 487次学习
-
- 从零制作响应式网站—Grid布局
- 本系列教程将展示从零制作一个假想的网络科技公司官网,分为导航,轮播,关于我们,成功案例,服务流程,团队介绍,数据部分,公司动态,底部信息等内容区块。网站整体采用CSSGrid布局,支持响应式,有流畅过渡和展现动画。
- 立即学习 485次学习