一句话定义:gzip 和 Brotli 是两种通用的无损(lossless)字节压缩算法,最常见的用法是在 HTTP 传输层把响应体压小再发出去。客户端收到后能一比一还原,一个字节都不差。
原理:先整理再发货。文本里充满了重复:JSON 同一批字段名反复出现、HTML 满是相似的标签、日志每行都带同样的时间戳格式。压缩做的事情很像打包行李:发现重复就“记账”,第一次原样写下,之后再遇到就用一个短标记指代(这是 LZ77 类算法的思路);然后把剩下的符号按出现频率重新分配码长,高频符号给短码、低频给长码(Huffman 编码)。Brotli 在这套机制之上还内置了一本“常用短语词典”,收录了大量 HTML、JS 和英文高频词,所以对中小体积的网页资源格外占便宜。
和相邻概念的区别。压缩(compression)不等于压缩代码:代码最小化(minification)是删掉空格和长变量名,让人写的源码变短;压缩是在字节层面重编码,内容不变。它也不等于模型量化(quantization)或蒸馏(distillation):后两者是有损的,会改变模型输出质量;gzip 和 Brotli 还原后与原文件比特级一致。另外,zstd 是同一家族里的另一位选手,特点是压缩速度和解压速度更快,很多场景在替代 gzip。
| 维度 | gzip | Brotli |
|---|---|---|
| 出身 | 老牌通用标准,几乎所有工具都支持 | 较新,浏览器与主流服务器普遍支持 |
| 压缩率 | 一般,够用 | 通常更好,小文本优势明显,内置词典有作用 |
| CPU 开销 | 低级别很轻 | 高级别明显更慢 |
| 典型用法 | 动态接口的默认选择 | 静态资源预压缩、文本型响应 |
为什么流式输出反而不能压。压缩需要“往后看”:算法要在一定长度的窗口里找重复,窗口越长压得越好,但这意味必须先攒够数据才能吐出去。而逐 token 的流式输出(SSE)每个数据块可能只有几十字节,还要求立刻发出;一旦为了及时发送而 flush,压缩块就提前结束,刚建立的字典作废,压缩头和块尾的固定开销反而让总字节变多。中间还有代理和网关的缓冲会破坏实时性。所以实时流通常干脆不开压缩。反过来,一次性返回的完整 JSON,比如一批检索结果或批量请求体,字段名高度重复,压缩收益非常可观,常见文本压到原体积的两三成是家常便饭。
压缩比与 CPU 的取舍。两种算法都有级别:gzip 从 1 到 9,Brotli 从 0 到 11,级别越高越费 CPU、越慢。工程上的常见做法是分两条路:静态资源在构建时用高级别预压缩好,运行时零成本直接发;动态接口用低级别,或者对小于 1KB 的响应干脆不压,因为头和计算的成本超过收益。已经压缩过的格式(JPEG、PNG、MP4、zip)再压基本无效,别浪费 CPU。
对从业者的实际意义。API 网关和数据接口默认开 gzip 几乎是零成本收益;面向浏览器的文本资源配 Brotli,注意它主要在现代浏览器走 HTTPS 时可用。客户端 SDK 要正确设置 Accept-Encoding 并处理解压。上线后看监控里的实际压缩率和 CPU 占用,比照理论值更有意义,具体支持范围以各服务器和浏览器官方文档为准。
