实测压缩需统一路径、验证解压正确性并覆盖多档数据:小数据防负增益,protobuf慎用gzip,zstd/snappy需调参,framing模式须跨语言一致,解压器应复用。

直接测,别信理论值——小数据用 snappy 可能比 gzip 还大,zstd 默认参数在 protobuf 上反而不如 zstd.WithEncoderLevel(1)。
用 bytes.Buffer + Compress() 统一测压缩后长度
所有算法必须走同一套输入/输出路径,否则 benchmark 失真。别用 gzip.NewWriter(io.Discard) 这类方式——它不写入实际数据,测不出真实体积。
- 输入统一用
[]byte,避免 string 转换引入额外开销 - 每个算法都 new 一个干净的
bytes.Buffer,调Compress()写入后取buf.Len(),不是buf.Bytes()长度(后者可能含未 flush 数据) - gzip 必须用
gzip.NewWriterLevel(buf, gzip.NoCompression)初始化,否则默认BestSpeed会偷偷加 header 开销 - zstd 要显式传
zstd.WithEncoderLevel(zstd.SpeedFastest),否则用默认 3 级,对小数据明显过重
注意小数据的“负增益”陷阱
100 字节 JSON 经 gzip 压缩后变 120 字节,不是 bug,是 deflate 协议头部硬开销(18–30 字节)。snappy 和 zstd 的 header 更小(zstd 1KB 输入下 header 仅 4 字节),但也要实测。
- 测试集至少覆盖三档:100B、2KB、50KB —— 小数据看 header,中数据看算法特性,大数据看吞吐
- protobuf 二进制数据通常比 JSON 密度高,
gzip压缩率会暴跌(实测 2KB protobuf 仅减 12%),而snappy仍稳在 28% - 别用
len(data) - len(compressed)算“节省”,直接比len(compressed),因为原始大小固定
解压正确性必须验证,否则压缩比无意义
压缩比再高,解出来是乱码或 panic,等于零。尤其 zstd 和 snappy 对 framing 模式敏感,跨语言互通时极易出错。
立即学习“go语言免费学习笔记(深入)”;
- 每次
Compress()后立刻Decompress(),比对bytes.Equal(original, decompressed) -
snappy在 kafka-go 默认用 standard framing,但nats.go用 raw framing;对接 Java 客户端时,kafka-go得换snappy.NewCompressionCodecFraming(snappy.Raw) -
zstd.Decompressor{}必须复用,每次 new 是性能黑洞,且可能因内部 state 不一致导致静默解压失败 - 解压前先检查输入长度:空或
len(input) 时直接返回 error,避免 <code>gzip: invalid header这类误导性错误
gRPC 或 HTTP 场景下,压缩比要叠加上下文开销
HTTP 只认 gzip 和 br,你用 zstd 再省 30%,客户端不支持就全白搭。gRPC 虽可扩展,但客户端和服务端必须同步注册 grpc.RPCCompressor 和 grpc.RPCDecompressor,漏一个就 fallback 到明文。
- HTTP 服务优先跑
gzip+compress/gzip标准库,兼容性第一 - 内网 gRPC 服务可上
zstd,但必须双注册:grpc.RPCCompressor和grpc.RPCDecompressor缺一不可,顺序无关 - 别用
zstd.Compressor{}—— 这是旧版 API,新版 gRPC 要求实现encoding.Compressor接口 - 客户端也得配
grpc.WithCompressor(grpc.NewCompressor("zstd", ...)),否则服务端即使支持也发明文
真正卡住人的从来不是“哪个算法快”,而是 header 开销、framing 对齐、注册遗漏和复用误用——这些地方一错,压缩比数字再好看也没用。


















