比较 ZSTD、Brotli 和 GZIP 在 API 中的表现:压缩速度、压缩比、CPU 成本,以及在生产环境中针对 JSON 与二进制负载的实用默认值。

API 响应压缩指的是服务器在发送之前将响应体(通常是 JSON)编码成更小的字节流。客户端(浏览器、移动应用、SDK 或其他服务)随后解压它。在 HTTP 中,这通过诸如 Accept-Encoding(客户端可解码的编码)和 Content-Encoding(服务器选择的编码)之类的头进行协商。
压缩主要带来三点好处:
权衡很直接:压缩节省带宽,但会消耗 CPU(压缩/解压),有时还需额外的 内存(缓冲)。是否值得取决于你的瓶颈所在。
压缩在以下场景表现最佳:
如果你返回大型 JSON 列表(目录、搜索结果、分析数据),压缩通常是最容易获得收益的方法之一。
当响应是:
在为 ZSTD vs Brotli vs GZIP 选择 API 压缩时,实用决策通常归结为:
本文余下内容都围绕为你的 API 与流量模式平衡这三点展开。
三者都能减少负载,但它们针对不同约束进行优化——速度、压缩比和兼容性。
ZSTD 的速度: 当你的 API 对尾延迟敏感或服务器受 CPU 约束时很合适。它能足够快地压缩,使得相较于网络时间的开销通常可以忽略,特别是针对中等到较大的 JSON 响应。
Brotli 的压缩比: 当带宽是主要约束(移动客户端、昂贵的出站、通过 CDN 交付)且响应以文本为主时表现最好。即便压缩耗时更长,也常常值得去换取更小的字节数。
GZIP 的兼容性: 当你需要最大程度的客户端支持且不能冒协商失败风险(老旧 SDK、嵌入式客户端、遗留代理)时最好使用它。它是一个安全的基线,尽管它不是性能最优。
压缩“级别”是一些预设,用来在CPU 时间和输出大小之间做权衡:
对于三种算法,解压通常比压缩便宜很多,但非常高的级别仍会增加客户端 CPU/电量消耗——这对移动端尤其重要。
压缩常被宣传为“更小的响应 = 更快的 API”。在慢速或昂贵网络上这通常成立——但并非必然。如果压缩增加了足够的服务器 CPU 时间,你可能会比未压缩时更慢——尽管线上的字节更少。
把成本分为两类有助于理解:
高压缩比可以减少传输时间,但如果压缩增加了(例如)每次响应 15–30 ms 的 CPU 时间,在快速连接上你可能得不偿失。
在负载下,压缩可能比 p50 更伤害 p95/p99 延迟。当 CPU 使用率飙升时,请求会排队。排队会把微小的每次请求开销放大成巨大的延迟——平均延迟看起来还好,但最慢的用户会受到影响。
不要凭猜测。做 A/B 测试或分阶段发布,比较:
用真实的流量模式和有效载荷测试。所谓“最佳”压缩级别是能减少总体时间的那个,而不仅仅是减少字节数。
压缩不是“免费”的——它把工作从网络转到服务器和客户端的 CPU/内存上。在 API 场景中,这表现为更长的请求处理时间、更大的内存占用,有时还会导致客户端变慢。
大部分 CPU 花费来自压缩响应。压缩需要发现模式、构建状态/字典并写出编码后的输出。
解压通常便宜得多,但仍值得注意:
如果你的 API 已经 CPU 密集(繁忙的应用服务器、复杂的认证、昂贵的查询),开启高压缩级别会在提升传输效率的同时,增加尾延迟。
压缩会以几种方式增加内存使用:
在容器化环境中,更高的峰值内存可能导致 OOM 杀死或更严格的限制,进而降低实例密度。
压缩增加了每次响应的 CPU 周期,从而降低了每个实例的吞吐。这可能更早触发自动扩缩,提高成本。一个常见模式是:带宽下降,但 CPU 开销上升——所以哪种资源稀缺决定了最佳选择。
在移动或低功耗设备上,解压会与渲染、JavaScript 执行和电池消耗竞争。一个能节省几个 KB 但解压更慢的格式在“可用数据时间”上会感觉更慢,特别是当响应需要立即使用时。
Zstandard(ZSTD)是一种现代压缩格式,旨在在不显著减慢 API 的前提下提供较强压缩比。对于许多以 JSON 为主的 API,它是一个强有力的“默认”:相比 GZIP 在相近或更低延迟下能明显减小响应,而且客户端解压非常快。
当你关心端到端时间而不仅仅是最小字节时,ZSTD 十分有价值。它通常压缩得快且解压极快——在每毫秒 CPU 时间都很关键的 API 场景下尤其适用。
它也在各种负载大小上表现良好:小到中等的 JSON 常能看到显著收益,而大型响应的收益通常更明显。
对大多数 API,从低级别(通常 1–3)开始。这些级别通常能提供最佳的延迟/大小权衡。
仅在以下情况下使用更高的级别:
务实的方法是设定较低的全局默认,然后对少数“大响应”端点选择性提高级别。
ZSTD 支持流式压缩,这能降低峰值内存并让大型响应更早开始发送。
字典模式对那些返回大量相似对象(重复键、稳定模式)的 API 有很大帮助。它在以下场景最有效:
在很多栈中服务器端支持比较直接,但客户端兼容性可能是决定因素。一些 HTTP 客户端、代理和网关默认还不会声明或接受 Content-Encoding: zstd。
如果你面向第三方消费者,保留一个回退(通常是 GZIP),并仅在 Accept-Encoding 明确包含时启用 ZSTD。
当响应是文本密集(JSON/GraphQL/XML/HTML)、中等到较大,并且用户处在慢速/昂贵网络或你为出站流量付费时,启用响应压缩通常是有价值的。对微小响应、已压缩的媒体(JPEG/MP4/ZIP/PDF)以及CPU 已成为瓶颈的服务,应跳过压缩或设置较高的阈值,以免增加 p95/p99 延迟。
因为压缩是在用CPU(有时还有内存)换取带宽。压缩时间会推迟服务器开始发送字节的时间(TTFB),而在负载下它会放大队列效应——经常导致尾延迟恶化,即使平均延迟有所改善。最优的设置是能减少端到端时间,而不仅仅是减少字节数。
一个实用的默认优先级常见为:
zstd 优先(快速、比率好)br(文本时通常最小,但可能更耗 CPU)gzip(兼容性最广)最终选择应基于客户端在 Accept-Encoding 中声明的能力,并保留安全回退(通常是 或 )。
从低级别开始并进行测量:
更高的级别通常带来递减的体积收益,但会显著增加 CPU 并可能恶化 p95/p99。
使用最小响应大小阈值以避免在微小负载上浪费 CPU:
按端点调优:比较节省的字节数、增加的服务器时间以及对 p50/p95/p99 的影响。
优先对结构化且重复性高的内容进行压缩:
压缩通过 HTTP 协商工作:
Accept-Encoding(例如 zstd, br, gzip)Content-Encoding 响应如果客户端未发送 Accept-Encoding,最安全的做法通常是不进行压缩。切勿返回客户端未声明可解码的 Content-Encoding,否则可能导致客户端无法解析响应体。
添加:
Vary: Accept-Encoding这样可以防止 CDN/代理缓存(例如)gzip 版本并错误地将其提供给未请求或无法解码 gzip(或 zstd/br)的客户端。如果你支持多种编码,该头对于缓存正确性至关重要。
常见的故障模式包括:
像对待性能特性一样发布压缩:
gzipidentity通常做法是仅对文本类 Content-Type 启用压缩,并对已知已压缩格式禁用压缩。
Content-EncodingAccept-Encoding)Content-Length 或代理问题)调试时,抓取原始响应头并用已知可靠的工具/客户端验证解压结果是关键。
若在负载下尾延迟上升,降低压缩级别、提高阈值或切换到更快的编解码(通常是 ZSTD)。