json序列化慢的主因是json.marshal反射开销大,字段多、嵌套深或含map/slice时更明显;应精简结构体、用匿名struct或生成工具如easyjson,流式写入优先选json.encoder,时间字段需自定义类型实现序列化方法。

JSON序列化慢,先看是不是用了 json.Marshal 直接序列化结构体字段太多
Go 默认的 json.Marshal 会反射遍历所有可导出字段,字段越多、嵌套越深,性能越差。尤其当结构体里混着 map[string]interface{} 或大 slice 时,反射开销会明显拖慢。
实操建议:
- 用
json.Marshal前确认结构体是否精简——删掉不用序列化的字段,或加json:"-"忽略 - 避免在 hot path(比如 HTTP handler 内)反复构造含大量字段的 struct 实例
- 如果只是输出固定几个字段,直接构建 map 或用
struct{ A, B string }匿名结构体,比复用大 struct 更快
需要高频 JSON 序列化?考虑用 easyjson 或 ffjson 替代原生 json
原生 json 包依赖反射,每次调用都做类型检查和字段查找;easyjson 则在编译期生成专用序列化函数,绕过反射,实测吞吐量常提升 2–5 倍。
但要注意:
- 必须给 struct 加
//easyjson:json注释,并运行easyjson -all xxx.go生成代码 - 生成的文件要和源码一起编译,不能只改 struct 就完事——漏掉重新生成会导致行为不一致
-
easyjson对嵌套泛型支持弱,map[any]any或interface{}字段仍会 fallback 到反射,别指望它全包圆
json.Encoder 比 json.Marshal 更适合流式写入
当你往 http.ResponseWriter 或文件写 JSON,别先 json.Marshal 成 []byte 再 write——这多一次内存拷贝,还可能触发 GC 压力。
使用 uber-go/dig 在 Golang 中实现依赖注入——基于反射容器,提供 Provide/Invoke、dig.In/dig.Out 参数与结果对象,支持命名依赖等
正确做法是直接用 json.NewEncoder(w io.Writer):
- 它把序列化结果直接写入底层
Writer,零中间分配 - 配合
http.Flusher还能边序列化边推送(适合长 JSON 流或 SSE) - 注意:如果
w是http.ResponseWriter,得确保没提前写 header,否则Encode可能 panic
时间字段序列化出错?别硬套 time.Time 默认格式
Go 的 time.Time 默认 JSON 序列化是 RFC3339 字符串,带毫秒和时区,但很多前端或老系统只认 "2006-01-02" 或 Unix timestamp。直接改 Time 字段 tag 很容易漏掉 UnmarshalJSON 配套逻辑,导致反序列化失败。
稳妥方案:
- 定义自定义类型封装
time.Time,实现MarshalJSON和UnmarshalJSON - 或者用
json.RawMessage延迟解析,避免在结构体层强绑定格式 - 千万别在 struct tag 里写
json:"created_at,string"然后以为能自动转字符串——它只对数字/布尔生效,对time.Time无效
真正卡住性能的往往不是 JSON 本身,而是没意识到 json.Marshal 的反射成本藏在哪一层;生成代码工具又容易漏掉重新生成步骤;时间字段看着简单,一动 tag 就埋下反序列化失败的坑。
golang免费学习笔记(深入):立即使用
在学习笔记中,你将探索golang的核心概念和高级技巧!










