压缩比计算必须调用gz.close()获取完整压缩数据并排除header干扰,原始大小应为[]byte长度而非string长度,json需用json.marshal()输出的[]byte计算。

压缩比不是看 len() 除一下就完事的——它必须在同一次压缩流程中捕获完整输出,且要排除 header 开销干扰才能反映真实收益。
压缩比计算必须调用 gz.Close() 才准确
很多人直接用 buf.Len() 在 gz.Write() 后取值,结果偏小甚至为 0。因为 gzip.Writer 是流式压缩器,未 Close() 或未 Flush() 时,压缩尾部(CRC、ISIZE)和部分缓冲数据根本没写入底层 bytes.Buffer。
- 必须显式调用
gz.Close()(或gz.Flush()+ 手动写 trailer,但不推荐) -
Close()不仅刷新数据,还会写入 8 字节 trailer(CRC32 + 原始长度),这是 Gzip 格式合法性的必要部分 - 若只测压缩速度而忽略
Close(),得到的字节数不能用于解压,也不代表真实网络传输体积
原始数据大小要用 []byte 长度,不是 string 长度
Go 中 string 是只读字节序列,len("中文") 返回的是 UTF-8 编码字节数(如 6),不是 rune 数(2)。压缩操作作用于字节流,所以原始大小必须是 len([]byte(s))。
- 错误写法:
len(s)—— 仅当字符串确定为 ASCII 时才等价 - 正确写法:
len([]byte(s))或直接对输入[]byte操作 - JSON 序列化后务必用
json.Marshal()输出的[]byte计算,别用fmt.Sprintf拼接后转 string 再转 byte
对比不同压缩级别时,注意 gzip.BestSpeed 的实际开销
设 level = gzip.BestSpeed(即 1)看似“快”,但对小数据(
- 测试阈值建议设在
512–2048字节之间,低于此值可跳过压缩 -
gzip.DefaultCompression(-1)通常是更稳的选择,平衡压缩率与 CPU - 不要对已压缩内容(如 base64 图片、Protobuf 序列化结果)再套 gzip,徒增 CPU 且无收益
真实对比示例:同一段 JSON 在 3 个级别下的压缩比
以下代码片段展示如何安全测量并打印压缩比(单位:%),输出类似:level=1 → 92.4% (1024→946 B):
func calcGzipRatio(data []byte, level int) float64 {
var buf bytes.Buffer
gz := gzip.NewWriter(&buf)
gz.SetLevel(level)
gz.Write(data)
gz.Close() // 关键
<pre class="brush:php;toolbar:false;">orig := len(data)
comp := buf.Len()
ratio := float64(comp) / float64(orig) * 100
fmt.Printf("level=%d → %.1f%% (%d→%d B)\n", level, ratio, orig, comp)
return ratio}
// 调用示例
data := []byte({"id":123,"name":"foo","tags":["a","b"]})
calcGzipRatio(data, gzip.BestSpeed) // level=1
calcGzipRatio(data, gzip.DefaultCompression) // level=-1
calcGzipRatio(data, gzip.BestCompression) // level=9
真正容易被忽略的是:header 开销不随数据变小而线性减少,所以压缩比曲线在小数据区是非单调的——有时候 level=1 比 level=9 还大。实测前务必覆盖你业务里典型的 payload 尺寸分布。
golang免费学习笔记(深入):立即使用
在学习笔记中,你将探索golang的核心概念和高级技巧!











