为什么 gob 序列化后的内容比 json序列化的还要大, 中间多了什么?

以下内容由 ai 生成并产生分析结果

测试脚本

  1
  2
  3
  4
  5
  6
  7
  8
  9
 10
 11
 12
 13
 14
 15
 16
 17
 18
 19
 20
 21
 22
 23
 24
 25
 26
 27
 28
 29
 30
 31
 32
 33
 34
 35
 36
 37
 38
 39
 40
 41
 42
 43
 44
 45
 46
 47
 48
 49
 50
 51
 52
 53
 54
 55
 56
 57
 58
 59
 60
 61
 62
 63
 64
 65
 66
 67
 68
 69
 70
 71
 72
 73
 74
 75
 76
 77
 78
 79
 80
 81
 82
 83
 84
 85
 86
 87
 88
 89
 90
 91
 92
 93
 94
 95
 96
 97
 98
 99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
package main

import (
	"bytes"
	"encoding/gob"
	"encoding/json"
	"fmt"
	"log"
)

// 测试用的结构体
type TestData struct {
	ID       int                    `json:"id"`
	Name     string                 `json:"name"`
	Email    string                 `json:"email"`
	Age      int                    `json:"age"`
	Active   bool                   `json:"active"`
	Tags     []string               `json:"tags"`
	Metadata map[string]interface{} `json:"metadata"`
}

func main() {
	// 创建测试数据
	testData := TestData{
		ID:     12345,
		Name:   "张三",
		Email:  "zhangsan@example.com",
		Age:    30,
		Active: true,
		Tags:   []string{"golang", "backend", "developer"},
		Metadata: map[string]interface{}{
			"department": "技术部",
			"level":      "高级工程师",
			"salary":     50000.0,
			"projects":   []string{"项目A", "项目B", "项目C"},
		},
	}

	// JSON 序列化
	jsonData, err := json.Marshal(testData)
	if err != nil {
		log.Fatal("JSON 序列化失败:", err)
	}

	// gob 序列化
	var gobBuffer bytes.Buffer
	gobEncoder := gob.NewEncoder(&gobBuffer)
	err = gobEncoder.Encode(testData)
	if err != nil {
		log.Fatal("gob 序列化失败:", err)
	}
	gobData := gobBuffer.Bytes()

	// 输出结果
	fmt.Println("=== 序列化大小比较 ===")
	fmt.Printf("JSON 序列化大小: %d 字节\n", len(jsonData))
	fmt.Printf("gob 序列化大小:  %d 字节\n", len(gobData))
	fmt.Printf("gob 比 JSON 大: %d 字节 (%.2f%%)\n", 
		len(gobData)-len(jsonData), 
		float64(len(gobData)-len(jsonData))/float64(len(jsonData))*100)

	fmt.Println("\n=== JSON 序列化内容 ===")
	fmt.Printf("JSON: %s\n", string(jsonData))

	fmt.Println("\n=== gob 序列化内容 (十六进制) ===")
	fmt.Printf("gob (hex): %x\n", gobData)

	fmt.Println("\n=== gob 序列化内容 (可打印字符) ===")
	printableGob := make([]byte, len(gobData))
	for i, b := range gobData {
		if b >= 32 && b <= 126 {
			printableGob[i] = b
		} else {
			printableGob[i] = '.'
		}
	}
	fmt.Printf("gob (printable): %s\n", string(printableGob))

	// 分析 gob 格式的额外开销
	analyzeGobOverhead()
}

func analyzeGobOverhead() {
	fmt.Println("\n=== gob 序列化额外开销分析 ===")
	
	// 测试简单数据类型
	testSimpleTypes()
	
	// 测试复杂数据类型
	testComplexTypes()
}

func testSimpleTypes() {
	fmt.Println("\n--- 简单数据类型比较 ---")
	
	// 整数
	intVal := 12345
	jsonInt, _ := json.Marshal(intVal)
	var gobIntBuf bytes.Buffer
	gob.NewEncoder(&gobIntBuf).Encode(intVal)
	fmt.Printf("整数 %d: JSON=%d字节, gob=%d字节\n", intVal, len(jsonInt), gobIntBuf.Len())
	
	// 字符串
	strVal := "Hello World"
	jsonStr, _ := json.Marshal(strVal)
	var gobStrBuf bytes.Buffer
	gob.NewEncoder(&gobStrBuf).Encode(strVal)
	fmt.Printf("字符串 \"%s\": JSON=%d字节, gob=%d字节\n", strVal, len(jsonStr), gobStrBuf.Len())
	
	// 布尔值
	boolVal := true
	jsonBool, _ := json.Marshal(boolVal)
	var gobBoolBuf bytes.Buffer
	gob.NewEncoder(&gobBoolBuf).Encode(boolVal)
	fmt.Printf("布尔值 %t: JSON=%d字节, gob=%d字节\n", boolVal, len(jsonBool), gobBoolBuf.Len())
}

func testComplexTypes() {
	fmt.Println("\n--- 复杂数据类型比较 ---")
	
	// 数组
	arrVal := []int{1, 2, 3, 4, 5}
	jsonArr, _ := json.Marshal(arrVal)
	var gobArrBuf bytes.Buffer
	gob.NewEncoder(&gobArrBuf).Encode(arrVal)
	fmt.Printf("整数数组: JSON=%d字节, gob=%d字节\n", len(jsonArr), gobArrBuf.Len())
	
	// Map
	mapVal := map[string]int{"a": 1, "b": 2, "c": 3}
	jsonMap, _ := json.Marshal(mapVal)
	var gobMapBuf bytes.Buffer
	gob.NewEncoder(&gobMapBuf).Encode(mapVal)
	fmt.Printf("字符串到整数的映射: JSON=%d字节, gob=%d字节\n", len(jsonMap), gobMapBuf.Len())
}

输出

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
=== 序列化大小比较 ===
JSON 序列化大小: 244 字节
gob 序列化大小:  358 字节
gob  JSON : 114 字节 (46.72%)

=== JSON 序列化内容 ===
JSON: {"id":12345,"name":"张三","email":"zhangsan@example.com","age":30,"active":true,"tags":["golang","backend","developer"],"metadata":{"department":"技术部","level":"高级工程师","projects":["项目A","项目B","项目C"],"salary":50000}}

=== gob 序列化内容 (十六进制) ===
gob (hex): 5a7f03010108546573744461746101ff800001070102494401040001044e616d65010c000105456d61696c010c000103416765010400010641637469766501020001045461677301ff820001084d6574616461746101ff8400000016ff81020101085b5d737472696e6701ff8200010c000027ff83040101176d61705b737472696e675d696e74657266616365207b7d01ff8400010c01100000ffcaff8001fe60720106e5bca0e4b88901147a68616e6773616e406578616d706c652e636f6d013c0101010306676f6c616e67076261636b656e6409646576656c6f7065720104056c6576656c06737472696e670c11000fe9ab98e7baa7e5b7a5e7a88be5b8880673616c61727907666c6f61743634080500fd6ae8400870726f6a65637473085b5d737472696e67ff821a000307e9a1b9e79bae4107e9a1b9e79bae4207e9a1b9e79bae430a6465706172746d656e7406737472696e670c0b0009e68a80e69cafe983a800

=== gob 序列化内容 (可打印字符) ===
gob (printable): Z.....TestData........ID.....Name.....Email.....Age.....Active.....Tags......Metadata.............[]string........'......map[string]interface {}................`r..........zhangsan@example.com.<.....golang.backend.developer...level.string....................salary.float64....j.@.projects.[]string............A.......B.......C.department.string..............

=== gob 序列化额外开销分析 ===

--- 简单数据类型比较 ---
整数 12345: JSON=5字节, gob=6字节
字符串 "Hello World": JSON=13字节, gob=15字节
布尔值 true: JSON=4字节, gob=4字节

--- 复杂数据类型比较 ---
整数数组: JSON=11字节, gob=23字节
字符串到整数的映射: JSON=19字节, gob=29字节

分析一下gob格式的内部结构

  1
  2
  3
  4
  5
  6
  7
  8
  9
 10
 11
 12
 13
 14
 15
 16
 17
 18
 19
 20
 21
 22
 23
 24
 25
 26
 27
 28
 29
 30
 31
 32
 33
 34
 35
 36
 37
 38
 39
 40
 41
 42
 43
 44
 45
 46
 47
 48
 49
 50
 51
 52
 53
 54
 55
 56
 57
 58
 59
 60
 61
 62
 63
 64
 65
 66
 67
 68
 69
 70
 71
 72
 73
 74
 75
 76
 77
 78
 79
 80
 81
 82
 83
 84
 85
 86
 87
 88
 89
 90
 91
 92
 93
 94
 95
 96
 97
 98
 99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
package main

import (
	"bytes"
	"encoding/gob"
	"encoding/json"
	"fmt"
)

func main() {
	fmt.Println("=== gob 序列化比 JSON 大的原因分析 ===\n")
	
	// 1. 类型信息开销
	analyzeTypeInformation()
	
	// 2. 字段名存储
	analyzeFieldNames()
	
	// 3. 编码格式差异
	analyzeEncodingFormat()
	
	// 4. 实际案例分析
	realWorldExample()
}

func analyzeTypeInformation() {
	fmt.Println("1. 类型信息开销")
	fmt.Println("   gob 需要在序列化数据中包含完整的类型定义信息")
	
	type SimpleStruct struct {
		Name string
		Age  int
	}
	
	data := SimpleStruct{Name: "test", Age: 25}
	
	// JSON 序列化
	jsonData, _ := json.Marshal(data)
	
	// gob 序列化
	var gobBuf bytes.Buffer
	gob.NewEncoder(&gobBuf).Encode(data)
	
	fmt.Printf("   简单结构体: JSON=%d字节, gob=%d字节\n", len(jsonData), gobBuf.Len())
	fmt.Printf("   JSON内容: %s\n", string(jsonData))
	fmt.Printf("   gob包含类型定义: SimpleStruct, Name string, Age int\n\n")
}

func analyzeFieldNames() {
	fmt.Println("2. 字段名存储方式")
	fmt.Println("   gob 存储完整的字段名,而 JSON 在每个对象中重复字段名")
	
	type Person struct {
		FirstName string
		LastName  string
		Email     string
	}
	
	// 单个对象
	single := Person{FirstName: "John", LastName: "Doe", Email: "john@example.com"}
	jsonSingle, _ := json.Marshal(single)
	var gobSingle bytes.Buffer
	gob.NewEncoder(&gobSingle).Encode(single)
	
	fmt.Printf("   单个对象: JSON=%d字节, gob=%d字节\n", len(jsonSingle), gobSingle.Len())
	
	// 多个对象数组
	multiple := []Person{
		{FirstName: "John", LastName: "Doe", Email: "john@example.com"},
		{FirstName: "Jane", LastName: "Smith", Email: "jane@example.com"},
		{FirstName: "Bob", LastName: "Johnson", Email: "bob@example.com"},
	}
	
	jsonMultiple, _ := json.Marshal(multiple)
	var gobMultiple bytes.Buffer
	gob.NewEncoder(&gobMultiple).Encode(multiple)
	
	fmt.Printf("   三个对象: JSON=%d字节, gob=%d字节\n", len(jsonMultiple), gobMultiple.Len())
	fmt.Printf("   JSON 重复字段名,gob 只存储一次类型定义\n\n")
}

func analyzeEncodingFormat() {
	fmt.Println("3. 编码格式差异")
	
	// 整数编码
	fmt.Println("   整数编码:")
	numbers := []int{0, 1, 127, 128, 255, 256, 65535, 65536}
	for _, num := range numbers {
		jsonData, _ := json.Marshal(num)
		var gobBuf bytes.Buffer
		gob.NewEncoder(&gobBuf).Encode(num)
		fmt.Printf("     %d: JSON=%d字节, gob=%d字节\n", num, len(jsonData), gobBuf.Len())
	}
	
	// 字符串编码
	fmt.Println("   字符串编码:")
	strings := []string{"", "a", "hello", "这是中文"}
	for _, str := range strings {
		jsonData, _ := json.Marshal(str)
		var gobBuf bytes.Buffer
		gob.NewEncoder(&gobBuf).Encode(str)
		fmt.Printf("     \"%s\": JSON=%d字节, gob=%d字节\n", str, len(jsonData), gobBuf.Len())
	}
	fmt.Println()
}

func realWorldExample() {
	fmt.Println("4. 实际案例分析")
	
	// 模拟你的测试文件中的数据结构
	type PLineData struct {
		ID                 int    `json:"id"`
		BusinessCode       string `json:"business_code"`
		BusinessName       string `json:"business_name"`
		FactorCode         string `json:"factor_code"`
		FactorName         string `json:"factor_name"`
		PlatformName       string `json:"platform_name"`
		WorkType           int    `json:"work_type"`
		PCode              string `json:"p_code"`
		PName              string `json:"p_name"`
		ProductionLineCode string `json:"production_line_code"`
		ProductionLineName string `json:"production_line_name"`
		IsEnable           int    `json:"is_enable"`
		IsDelete           int    `json:"is_delete"`
		CreateTime         string `json:"create_time"`
		UpdateTime         string `json:"update_time"`
		PlineStatus        int    `json:"pline_status"`
		PlatformCode       string `json:"platform_code"`
	}
	
	// 创建测试数据
	data := PLineData{
		ID:                 389,
		BusinessCode:       "P",
		BusinessName:       "POI",
		FactorCode:         "P_1003",
		FactorName:         "上车点_区域型",
		PlatformName:       "xx平台",
		WorkType:           2,
		PCode:              "P_110093",
		PName:              "POI上车点区域型",
		ProductionLineCode: "101_688_1",
		ProductionLineName: "打车上车点产线-打车上车点产线1-区域型上车点",
		IsEnable:           1,
		IsDelete:           0,
		CreateTime:         "2025-01-20 18:42:15",
		UpdateTime:         "2025-01-20 18:42:15",
		PlineStatus:        1,
		PlatformCode:       "11",
	}
	
	jsonData, _ := json.Marshal(data)
	var gobBuf bytes.Buffer
	gob.NewEncoder(&gobBuf).Encode(data)
	
	fmt.Printf("   产线数据结构: JSON=%d字节, gob=%d字节\n", len(jsonData), gobBuf.Len())
	fmt.Printf("   gob 额外开销: %d字节 (%.1f%%)\n", 
		gobBuf.Len()-len(jsonData), 
		float64(gobBuf.Len()-len(jsonData))/float64(len(jsonData))*100)
	
	fmt.Println("\n=== gob 额外开销的组成 ===")
	fmt.Println("1. 类型定义信息: 包含结构体名称和所有字段的类型信息")
	fmt.Println("2. 字段名称: 完整的字段名存储")
	fmt.Println("3. 类型标识符: 用于标识不同的数据类型")
	fmt.Println("4. 长度前缀: 变长编码的长度信息")
	fmt.Println("5. 版本信息: gob 格式的版本标识")
	
	fmt.Println("\n=== 何时 gob 更有优势 ===")
	fmt.Println("1. 大量相同结构的数据: 类型信息只需存储一次")
	fmt.Println("2. 复杂嵌套结构: gob 的二进制格式更紧凑")
	fmt.Println("3. Go 程序间通信: 无需解析,直接反序列化")
	fmt.Println("4. 保持类型安全: 编译时类型检查")
}

输出:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
=== gob 序列化比 JSON 大的原因分析 ===

1. 类型信息开销
   gob 需要在序列化数据中包含完整的类型定义信息
   简单结构体: JSON=24字节, gob=55字节
   JSON内容: {"Name":"test","Age":25}
   gob包含类型定义: SimpleStruct, Name string, Age int

2. 字段名存储方式
   gob 存储完整的字段名,而 JSON 在每个对象中重复字段名
   单个对象: JSON=64字节, gob=91字节
   三个对象: JSON=200字节, gob=171字节
   JSON 重复字段名,gob 只存储一次类型定义

3. 编码格式差异
   整数编码:
     0: JSON=1字节, gob=4字节
     1: JSON=1字节, gob=4字节
     127: JSON=3字节, gob=5字节
     128: JSON=3字节, gob=6字节
     255: JSON=3字节, gob=6字节
     256: JSON=3字节, gob=6字节
     65535: JSON=5字节, gob=7字节
     65536: JSON=5字节, gob=7字节
   字符串编码:
     "": JSON=2字节, gob=4字节
     "a": JSON=3字节, gob=5字节
     "hello": JSON=7字节, gob=9字节
     "这是中文": JSON=14字节, gob=16字节

4. 实际案例分析
   产线数据结构: JSON=471字节, gob=503字节
   gob 额外开销: 32字节 (6.8%)

=== gob 额外开销的组成 ===
1. 类型定义信息: 包含结构体名称和所有字段的类型信息
2. 字段名称: 完整的字段名存储
3. 类型标识符: 用于标识不同的数据类型
4. 长度前缀: 变长编码的长度信息
5. 版本信息: gob 格式的版本标识

=== 何时 gob 更有优势 ===
1. 大量相同结构的数据: 类型信息只需存储一次
2. 复杂嵌套结构: gob 的二进制格式更紧凑
3. Go 程序间通信: 无需解析,直接反序列化
4. 保持类型安全: 编译时类型检查

总结

通过实际测试和分析,我发现 gob 序列化后的内容比 JSON 序列化的大 主要有以下几个原因:

  1. 类型信息开销 gob 需要在序列化数据中包含完整的类型定义信息,包括: 结构体名称 每个字段的名称和类型 类型标识符和版本信息 JSON 只存储数据值,不包含类型信息
  2. 编码格式差异 gob 使用二进制格式,但包含额外的元数据: 长度前缀(变长编码) 类型标识符 字段映射信息 JSON 使用文本格式,相对简洁
  3. 字段名存储方式 gob 在类型定义中存储完整的字段名 JSON 在每个对象中重复字段名
  4. 实际测试结果 从测试结果可以看出: 简单数据:gob 比 JSON 大 46.72% 复杂结构:gob 比 JSON 大 6.8% 多个相同结构对象:gob 可能比 JSON 更小(因为类型信息只存储一次)
  5. gob 的优势场景 虽然 gob 通常更大,但在以下情况下更有优势: 大量相同结构的数据:类型信息只需存储一次 Go 程序间通信:无需解析,直接反序列化,性能更好 类型安全:编译时类型检查 复杂嵌套结构:在某些情况下二进制格式更紧凑
  6. 建议 如果主要关注存储空间和跨语言兼容性,使用 JSON 如果主要关注性能和类型安全(Go 程序间通信),使用 gob 对于大量相同结构的数据,gob 可能更有优势