压缩比本质是原始数据与压缩后数据字节长度的比值,需用len([]byte)准确获取二者真实长度,手动计算float64(originalsize)/float64(compressedsize),并固定gzip压缩级别以确保结果可复现。

压缩比计算的本质是两个字节长度的比值
压缩比不是某个 Go 标准库函数直接返回的值,而是你手动算出来的:float64(originalSize) / float64(compressedSize)。关键在于准确获取原始数据和压缩后数据的真实字节长度——不是文件大小,不是字符串长度,是 []byte 的 len() 或写入后的实际字节数。
常见错误是用 len(str) 算字符串长度代替原始字节长度(UTF-8 下中文会出错),或用 os.Stat().Size() 读空文件、未 flush 的 buffer 导致 compressedSize 偏小。
- 原始数据务必转为
[]byte后取len(),例如data := []byte("hello 世界"),别用len("hello 世界") - 压缩后数据必须从底层 writer 获取真实写入量:用
bytes.Buffer接收,再调b.Len();不要依赖gzip.Writer.Close()后的文件 stat - 如果压缩目标是文件流(如 HTTP body),需在写入完成、
Flush()和Close()之后再读 buffer 长度
用 compress/gzip 实现可复现的压缩比测量
Go 标准库的 gzip 是最常用选择,但默认压缩级别影响结果——不显式设置会导致不同 Go 版本或运行环境行为不一致。必须固定 gzip.BestSpeed、gzip.BestCompression 或 gzip.DefaultCompression。
下面这段代码能稳定输出压缩比:
func calcGzipRatio(data []byte) (float64, error) {
var buf bytes.Buffer
gw, err := gzip.NewWriterLevel(&buf, gzip.BestCompression)
if err != nil {
return 0, err
}
if _, err := gw.Write(data); err != nil {
gw.Close()
return 0, err
}
gw.Close() // 必须 close 才能完成压缩并写入尾部
return float64(len(data)) / float64(buf.Len()), nil
}
-
gzip.NewWriterLevel第二个参数不能省略,默认值在不同 Go 小版本中可能变化 -
gw.Close()不仅释放资源,还强制写出压缩尾部(如 ISIZE 字段),漏掉会导致buf.Len()偏小、压缩比虚高 - 别用
io.Copy(gw, bytes.NewReader(data))替代gw.Write()—— 它不会提前触发内部 flush,且对小数据无优势
对比 compress/zlib 和 compress/flate 的适用场景
zlib 和 flate 的压缩算法核心相同(DEFLATE),但封装协议不同:zlib 多一层头尾校验,flate 是裸 DEFLATE 流。这意味着同一批数据,zlib 输出一定比 flate 大 6 字节左右(固定头 + Adler32)。
选哪个取决于你要测什么:
- 测「HTTP 响应压缩」或「PNG 内部压缩」:用
compress/zlib,因为它们实际用的就是 zlib 格式 - 测「纯算法极限」或对接某些嵌入式协议:用
compress/flate,它更轻、无协议开销 -
compress/gzip比zlib多 18 字节左右(gzip header + trailer),适合测文件归档类场景
三者不能混用计算——用 gzip 压缩的数据,不能拿 zlib 解,否则报 zlib: invalid header。
注意小数据和重复模式对压缩比的误导性
小于 20 字节的数据,gzip/zlib 往往越压越大(头部开销 > 压缩收益);而全相同字符(如 10KB 的 "a")会得到极高压缩比,但这在真实业务数据中极少出现。
做压缩比应用时,必须明确测试数据分布:
- 避免单次调用测单个短字符串——改用至少 1KB 以上的典型样本(如 JSON 日志片段、HTML 片段)
- 批量跑多个样本取平均值,比单次结果更有参考价值
- 如果目标是优化 API 响应,直接抓线上真实 response body 二进制 dump 来测,别用合成数据
压缩比数字本身容易让人忽略上下文:一个 95% 的压缩比(即原大小 / 压缩后 = 20x)听着很美,但如果原始数据只有 100B,压缩后仍是 5B,实际节省不到 1KB——这种“高比低效”最容易在初期被误判。
golang免费学习笔记(深入):立即使用
在学习笔记中,你将探索golang的核心概念和高级技巧!











