必须调用 Flush 和 Close 后才能用 buf.Len() 获取真实压缩体积,否则结果偏小或不兼容;压缩前应判断数据长度(如≥1KB才值得压),并单独测试 payload 而非 HTTP 响应流。

用 bytes.Buffer 捕获压缩输出再比长度
压缩后体积不能靠 len([]byte) 直接算——因为 gzip.Writer 是流式压缩,数据还在缓冲区里没落盘。必须用 bytes.Buffer 作为目标写入器,等压缩完成后再读它的长度。
关键动作有三个:调 Write、必须 Flush(确保压缩数据全部写出)、最后 Close(写 gzip trailer)。漏掉 Flush 会导致压缩后体积偏小,漏掉 Close 可能丢失校验尾部,影响解压兼容性。
-
buf.Len()就是压缩后真实字节数,可直接用于计算体积比 - 原始字节数用
len(inputBytes),别用len(inputString)(UTF-8 多字节字符会误算) - 压缩前先判断数据是否值得压:比如小于 200 字节的 JSON,gzip 后大概率更大
别在 http.ResponseWriter 上直接测体积比
HTTP 响应体压缩受协商逻辑干扰:要检查 Accept-Encoding、设置 Vary 头、跳过 204/304 等无 body 状态码。直接在 handler 里测,容易把 header 开销、chunked 编码、early write 等因素混进来,算出来的“压缩比”失真。
真正该测的是 payload 本身——也就是你打算塞进 ResponseWriter.Write() 的那块 []byte。把它抽出来,单独走一遍 gzip.NewWriter(&buf) 流程,结果才干净可比。
立即学习“go语言免费学习笔记(深入)”;
- 避免在中间件里边写响应边统计,header 和 body 分离不清晰
- 如果一定要在 HTTP 场景下监控,建议用
io.MultiWriter把响应体同时写进bytes.Buffer和原始ResponseWriter,但要注意并发安全
体积比公式和临界值要带单位、看场景
体积比 = float64(buf.Len()) / float64(len(inputBytes)),结果是个小数。0.3 表示压缩后只剩原始 30%,即压缩率 70%;大于 1.0 就说明白压了——这在小数据上很常见。
gzip 对短文本有固定头部开销(约 18–30 字节),所以:
- 输入 1.0 的概率超过 80%
- 输入在 200–500 字节之间,体积比集中在 0.9–1.1,收益极不稳定
- 输入 ≥ 1KB,gzip 才大概率稳定降到 0.4–0.6 区间
如果你压的是 protobuf 或已加密二进制,体积比可能长期卡在 0.95 附近——不是代码问题,是数据本身熵太高,算法无能为力。
测耗时得包住 Flush 和 Close
只计 Write 耗时不全面:Flush 才真正触发压缩计算,Close 要写 trailer 和校验和。三者加起来才是真实开销。
实测中,Write 占总耗时不到 10%,剩下 90% 都在 Flush + Close。尤其在启用 gzip.BestCompression 时,Close 可能占到 70% 以上。
- 用
time.Now()包住整个流程:从gzip.NewWriter创建后开始,到gz.Close()返回结束 - 别用
runtime.GC()干扰测量,压缩过程本身不触发 GC - 压同一段数据跑 10 次取平均值,避开单次调度抖动
体积比数字本身不难算,难的是搞清它到底在反映什么——是算法能力?数据特性?还是你选错了压缩时机。小数据硬压、已压缩数据再压、没 flush 就读长度,这三个动作一做,比出来的数就全废了。


















