应替换 json.Unmarshal 为 easyjson 或 go-json:easyjson 需添加 //easyjson:json 注释并导出字段,生成无反射解码;go-json 兼容性更好,需导出字段并显式 tag,配合流式解码和 sync.Pool 复用可显著降 CPU 与内存。

直接换掉 json.Unmarshal,别在运行时查字段
为什么 json.Unmarshal 一调用就占 CPU 30%+
不是你结构体写得不对,而是标准库每次都要:用 reflect.Value.FieldByName 找字段、检查 json:"id" 标签、判断指针是否为 nil、做 UTF-8 转义、拼接字节。字段超 10 个或嵌套 ≥3 层时,反射成本指数上升;若结构体里含 map[string]interface{} 或 interface{},它会直接 fallback 到最慢路径。
pprof 里看到 encoding/json.(*decodeState).object 或 reflect.Value.Interface 占高,基本就能确认瓶颈在这儿。
用 easyjson 生成无反射的 UnmarshalJSON
它把字段访问、类型转换、标签解析全挪到构建阶段,运行时只剩纯内存读写 + strconv 拼接,不碰 reflect 一丁点。
立即学习“go语言免费学习笔记(深入)”;
-
//easyjson:json必须独占一行,写在结构体定义正上方 - 字段必须导出(首字母大写),且带显式
json:"field"标签 - 运行
easyjson -all user.go,生成user_easyjson.go - 框架中仍用
c.ShouldBindJSON(&u),只要User实现了生成的UnmarshalJSON方法,Gin/Echo 就自动走优化路径 - 别在结构体里嵌套未加注释的匿名 struct,也别用
interface{}字段——否则生成失败或 panic
用 go-json 替代标准库(兼容性更强)
它不强制改结构体注释,但要求字段导出 + 显式 tag,否则仍走反射。适合已有大量结构体、不想批量加注释的项目。
- 导入替换:
import json "github.com/goccy/go-json" - 解码时别再用
json.Unmarshal(b, &v),改用json.NewDecoder(r.Body).Decode(&v),支持流式读取,省掉中间[]byte分配 -
*json.Decoder实例可复用:decoder.Reset(r.Body)比新建快得多 - 若字段含
json.RawMessage,注意它引用原始 buffer;后续要多次解析,得先append([]byte(nil), raw)拷贝一份,否则 buffer 复用后内容失效
别让上游数据构造毁掉所有优化
很多服务换了 easyjson 或 go-json 后 QPS 没涨,问题根本不在 Unmarshal 这一行——而是在前面几十行代码里:
- 避免循环中拼
map[string]interface{}再传给json.Unmarshal,改用预定义 struct - 别把整个 request body 读成
[]byte再解码;用json.NewDecoder(c.Request.Body)直接流式解,内存峰值降 60%+ - 高频接口中,用
sync.Pool复用bytes.Buffer或解码器实例,防止短生命周期对象逃逸到堆上
真正卡住吞吐的,往往不是最后一行 Unmarshal,而是你还没意识到的那几行“临时凑数”的 map 构造和 byte 拷贝。



















