Go 語言 JSON 解析屆頂流:Sonic
Sonic 是字節跳動開源的一款 Go 語言 JSON 解析庫,按照官方的說法:
-
Sonic 是一個速度奇快的 JSON 序列化 / 反序列化庫,由 JIT (即時編譯)和 SIMD (單指令流多數據流)加速。
-
對於所有大小的 json 和所有使用場景, Sonic 表現均爲最佳。
自 2021 年 7 月份發佈以來,Sonic 已被抖音、今日頭條等業務採用,累計爲字節跳動節省了數十萬 CPU 核(省了好多錢啊~)。
下面我們來看 Sonic 相比其他 JSON 解析庫優勢有多大,以及常見的使用舉例。
Sonic 研發背景
-
Go 本身自帶標準 JSON 庫:encoding/json,另外還有很多優秀的第三方庫,比如:Json-iterator、Easyjson、Gjson、Sjson 等,其中 Json-iterator 最受歡迎(12.3+k Star)。
-
字節根據樣本 JSON 的 key 數量和深度分爲三個量級:
-
小(small):400B,11 key,深度 3 層;
-
中(medium):110KB,300+ key,深度 4 層(實際業務數據,其中有大量的嵌套 JSON string);
-
大(large):550KB,10000+ key,深度 6 層。
測試結果如下:目前這些 JSON 庫均無法在各場景下都保持最優性能,即使是當前使用最廣泛的第三方庫 Json-iterator,在泛型編解碼、大數據量級場景下的性能也滿足不了字節的需求。
JSON 庫的基準編解碼性能固然重要,但是對不同場景的最優匹配更關鍵 —— 於是字節走上了自研 JSON 庫的道路。
Sonic 性能測試
以下是字節根據上面的不同場景進行的測試結果:
可以看到 Sonic 在大部分場景下都有明顯的優勢:
通過上面的測試比較結果,我們很難不想去使用 Sonic 去優化我們的業務,爲我們自己的系統提效添磚加瓦~
Sonic 常見使用場景
解析爲 map 類型
使用 Sonic 將 JSON 數據解析爲 map 類型的方法:
- 只需要調用 Sonic 的 Unmarshal 函數,並將 JSON 數據和一個空的 map 作爲參數傳遞即可。
例如:
package main
import (
"github.com/bytedance/sonic"
"fmt"
)
func main() {
var jsonStr = `{"name": "Harper", "age": 18}`
var data map[string]any
err := sonic.Unmarshal([]byte(jsonStr), &data)
if err != nil {
fmt.Println("解析失敗:", err)
}
fmt.Println(data["name"], data["age"])
}
解析爲結構體類型
使用 Sonic 將 JSON 數據解析爲結構體類型的方法:
- 只需要定義一個結構體,用於存儲解析結果,並將 JSON 數據和結構體作爲參數傳遞給 Unmarshal 函數即可。
例如:
package main
import (
"github.com/bytedance/sonic"
"fmt"
)
func main() {
type Person struct {
Name string `json:"name"`
Age int `json:"age"`
}
var jsonStr = `{"name": "Harper", "age": 20}`
var person Person
err := sonic.Unmarshal([]byte(jsonStr), &person)
if err != nil {
fmt.Println("解析失敗:", err)
}
fmt.Println(person.Name, person.Age)
}
解析嵌套 JSON 數據
Sonic 可以解析嵌套的 JSON 數據,其實跟上面的「解析爲結構體類型」原因一樣,例如:
package main
import (
"github.com/bytedance/sonic"
"fmt"
)
func main() {
var jsonStr = `{"name": "Harper", "age": 30, "address": {"city": "HaiDian Beijing", "country": "China"}}`
type Address struct {
City string `json:"city"`
Country string `json:"country"`
}
type Person struct {
Name string `json:"name"`
Age int `json:"age"`
Address Address `json:"address"`
}
var person Person
err := sonic.Unmarshal([]byte(jsonStr), &person)
if err != nil {
fmt.Println("解析失敗:", err)
}
fmt.Println(person.Name, person.Age, person.Address.City, person.Address.Country)
}
解析數組類型
Sonic 還可以解析數組類型的 JSON 數據,例如:
package main
import (
"github.com/bytedance/sonic"
"fmt"
)
func main() {
var jsonStr = `[{"name": "Harper", "age": 33}, {"name": "Bella", "age": 34}]`
type Person struct {
Name string `json:"name"`
Age int `json:"age"`
}
var persons []Person
err := sonic.Unmarshal([]byte(jsonStr), &persons)
if err != nil {
fmt.Println("解析失敗:", err)
}
for _, person := range persons {
fmt.Println(person.Name, person.Age)
}
}
小結
Sonic 是一款高性能的 JSON 解析庫,它提供了豐富的 JSON 解析 API,包括解析爲 map、結構體、嵌套 JSON 數據和數組類型等,涵蓋了我們工作中幾乎所有的應用場景。
特別是對於大數據解析的場景,如果我們系統遇到了 JSON 數據解析的瓶頸,不妨試試 Sonic,看看效果,畢竟替換很方便,跟 Json-iterator 類似,平替成本很低。
【參考資料】
-
https://github.com/bytedance/sonic/blob/main/README_ZH_CN.md
-
https://xie.infoq.cn/article/d144592e5aec0179cafc5cf1b
本文由 Readfog 進行 AMP 轉碼,版權歸原作者所有。
來源:https://mp.weixin.qq.com/s/Ij5wNjNZ6rRbQqTYIvP_aw