Golang - json 性能分析

  Json 作为一种重要的数据格式,具有良好的可读性以及自描述性,广泛地应用在各种数据传输场景中。Go 语言里面原生支持了这种数据格式的序列化以及反序列化,内部使用反射机制实现,性能有点差,在高度依赖 json 解析的应用里,往往会成为性能瓶颈,从下面的火焰图中可以发现在业务逻辑处理中,有一半多的性能消耗都是在 JSON 解析过程中,有很多第三方库帮忙解决了这个问题,接下来分析一下常用的几个库。

encoding/json

  • 官方提供的标准json, 实现RFC 7159中定义的JSON编码和解码。
  • 使用的时候需要预定义struct,原理是通过reflection和interface来完成工作,性能低。

常用接口:

  • func Marshal(v interface{}) ([]byte, error) 生成JSON
  • func Unmarshal(data []byte, v interface{}) error 解析JSON到struct

json-iterator

使用modern-go/reflect2来优化反射性能,通过大幅度减少反射操作来提高速度。

  • 完全兼容json标准库,也就是API用法完全一样,原有代码不需要改动。
  • 提供了一个兼容模式(需要手动开启),可以自动转换字符串/数字弱类型问题,可以转换[]与{}弱类型问题(PHP中的array问题)。

Github: https://github.com/json-iterator/go

package main
import (
    "fmt"
    jsoniter "github.com/json-iterator/go"
    "github.com/json-iterator/go/extra"
)
var json = jsoniter.ConfigCompatibleWithStandardLibrary
func init() {
    // RegisterFuzzyDecoders decode input from PHP with tolerance.
    //  It will handle string/number auto conversation, and treat empty [] as empty struct.
    extra.RegisterFuzzyDecoders() // 手动开启兼容模式
}
type StdStruct struct {
    Age int `json:"age"`
}
func main() {
    s := "{\"age\": \"10\"}"
    d := &StdStruct{}
    if err := json.Unmarshal([]byte(s), d); err != nil {
        fmt.Println(err)
    } else {
        fmt.Println(d.Age)  // 开启兼容模式后,可以解析出字符串下的10
    }
}

注意:只需要在main文件里通过init开启1次PHP兼容模式即可,后续引入的模块不需要重复开启。

以下是对一个对象序列化1000次,所用的耗时对比:

easyjson

easyjson 的思想是增加一个预编译的过程,预先生成对应结构的序列化反序列化代码,除此之外,easyjson 还放弃了一些原生库里面支持的一些不必要的特性,比如:key 类型声明,key 大小写不敏感等等,以达到更高的性能

生成代码执行 easyjson -all <file.go> 即可,如果不指定 -all 参数,只会对带有 //easyjson:json 的结构生成代码

//easyjson:json
type A struct {
    Bar string
}

性能测试

 从上面的结果可以看出来:

  • easyjson 无论是序列化还是反序列化都是最优的,序列化提升了1倍,反序列化提升了3倍
  • jsoniter 性能也很好,接近于easyjson,关键是没有预编译过程,100%兼容原生库
  • ffjson 的序列化提升并不明显,反序列化提升了1倍
  • codecjson 和原生库相比,差不太多,甚至更差
  • jsonparser 不太适合这样的场景,性能提升并不明显,而且没有反序列化

所以综合考虑,建议使用 jsoniter,如果追求极致的性能,考虑 easyjson。

posted @ 2024-06-01 23:08  李若盛开  阅读(109)  评论(0编辑  收藏  举报