一、zstd 是什么
zstd(Zstandard) 是 Facebook(Meta)于 2016 年开源的通用无损压缩算法库,作者是 Yann Collet------没错,就是著名极速压缩库 LZ4 的作者。它采用 BSD 协议开源,你可以放心地用在自己的商业项目里,无需任何授权费。
一句话概括:zstd 用接近 zlib 的压缩比,跑出了远超 zlib 的速度,而且还能通过"字典"把几字节的小消息压得比谁都快。
通俗类比:
- 传统压缩库(zlib)像一辆"满载卡车"------装得多,但启动慢、跑得慢;
- LZ4 像一辆"赛车"------跑得飞快,但装不了多少;
- zstd 像一辆"高铁"------跑得快(速度远超卡车)、装得也不少(压缩比不输卡车),还能通过字典"提前预习"你的数据格式,对小数据尤其友好。
zstd 的江湖地位有多高?Linux 内核、Ubuntu 的 apt/dpkg、RocksDB、ClickHouse、Parquet、Kafka(可选)、Node.js 生态、微信/QQ 的传输协议......大量重量级项目都在用它。它已经成为现代数据压缩的事实标准之一。
二、为什么选 zstd:五大优点
优点 1:速度与压缩比的"黄金平衡"
| 指标 | zstd | zlib(gzip 同级) | LZ4 |
|---|---|---|---|
| 压缩速度 | 快(默认级别下比 zlib 快 3~8 倍) | 慢 | 极快 |
| 解压速度 | 极快(接近 LZ4) | 慢 | 极快 |
| 压缩比 | 与 zlib 相当甚至略优 | 基准 | 明显偏低 |
zstd 采用熵编码(Huffman + FSE)+ LZ77 类匹配的混合架构,配合大量的工程优化(如多级哈希表、块级并行、SIMD 加速),在默认级别(3)下就能同时做到"比 zlib 快好几倍"和"压缩比不输 zlib"。
优点 2:压缩级别 1~22 随便调
zstd 提供了 ZSTD_minCLevel()(-131072,极速模式)到 ZSTD_maxCLevel()(22,极致压缩)的超宽调节范围:
- 级别 1~3:追求速度,适合日志、缓存、实时传输;
- 级别 5~9:速度/压缩比均衡,日常首选;
- 级别 10~19:更高压缩比,适合离线归档;
- 级别 20~22:--ultra 级别,压缩极慢但压缩比最高,适合一次性打包。
一次压缩、任意版本都能解压(zstd 的帧格式向后兼容),你完全可以在不同环节用不同级别,不用担心格式分裂。
优点 3:字典压缩,小数据的救星
这是 zstd 的杀手锏。对几百字节的 JSON 消息、日志行这类小数据,普通压缩算法基本压不动(头开销都占了),但 zstd 可以先用一组同类型样本训练出"字典",之后每条小消息都能用字典做参考,压缩比和速度都大幅提升。微信、游戏服务器的心跳包、遥测数据、大量小文件的传输,全靠这一手。
优点 4:多线程压缩开箱即用
只需设置一个参数 ZSTD_c_nbWorkers,zstd 就会自动把大块数据切成多个块并行压缩,吃满多核 CPU。无需你自己设计分块、拼接逻辑------它是官方的原生能力,稳定且经过生产验证。
优点 5:API 分层清晰,嵌入式友好
zstd 提供三层 API:
- Simple API:ZSTD_compress / ZSTD_decompress,一行代码搞定内存到内存的压缩;
- Advanced API:ZSTD_CCtx 上下文复用、参数精细控制;
- Streaming API:ZSTD_compressStream2,处理任意大小的流式数据。
核心库是纯 C 实现的单文件风格,也可编译成静态库,无第三方依赖,编译产物小,非常适合嵌入式、移动端和服务端场景。
三、适用场景:什么时候该用它
场景 1:日志与遥测数据的压缩归档
服务器日志量大、重复率高、需要长期保留。用 zstd 压缩后落盘,能省 80%+ 磁盘空间,且压缩时 CPU 开销可控。
场景 2:网络传输(HTTP / RPC / 消息队列)
在带宽受限的移动网络或跨机房链路上,用 zstd 压缩请求/响应体,能显著降低传输时延和流量费用。很多现代 RPC 框架和消息队列(如 Kafka、gRPC 可选压缩)都内置了 zstd 支持。
场景 3:游戏与客户端资源包
游戏材质、模型、音频等资源文件适合离线用高压缩级别(19+)打包,运行时再用极速解压(zstd 解压极快,几乎不影响加载体验)。
场景 4:数据库与存储引擎
RocksDB、ClickHouse、MongoDB 等存储引擎都支持 zstd 作为压缩算法:既比 LZ4 省空间,又比 zlib 快,是"容量与性能折中"的首选。
场景 5:高频小消息(配合字典)
游戏心跳包、IoT 设备遥测、金融行情 tick------这类只有几十到几百字节的消息,用 zstd 字典压缩后往往能从几百字节压到几十字节,特别适合高 QPS 场景。
场景 6:系统级与工具链
Linux 内核(Btrfs、zram)、apt/dpkg 软件包、npm 包分发等系统级场景都用了 zstd------你日常用的 Ubuntu 软件源加速就是它的功劳。
四、环境准备:三步装好 zstd
第 1 步:选择安装方式(任选其一)
方式 A:vcpkg(Windows 首选)
vcpkg install zstd:x64-windows
方式 B:apt(Ubuntu/Debian)
sudo apt install libzstd-dev
方式 C:源码编译(追求最新版)
git clone https://github.com/facebook/zstd.git
cd zstd
make -j$(nproc)
sudo make install
第 2 步:确认安装成功
bash
# 命令行工具自带,可快速验证
zstd --version
第 3 步:在 CMake 中链接(推荐)
bash
find_package(zstd REQUIRED) # 或直接 target_link_libraries(... zstd::libzstd_shared)
target_link_libraries(your_target PRIVATE zstd::libzstd_shared)
⚠️ 如果 find_package(zstd) 找不到,检查 vcpkg/apt 安装的包是否包含 CMake config 文件(zstdConfig.cmake);源码编译安装默认会生成。
五、具体使用方式:从入门到进阶
下面所有代码都以 zstd 1.5.x 的稳定 API 为例,均包含中文注释,可直接编译运行。
第 1 步:一次性压缩/解压(Simple API)
这是最简单的入门路径:数据整体在内存中,一次压缩、一次解压。
cpp
// simple_demo.cpp ------ zstd 入门:内存到内存的压缩与解压
#include <zstd.h>
#include <cstdio>
#include <cstring>
#include <string>
#include <vector>
// 打印 zstd 错误信息并退出
static void die(const char* msg, size_t code) {
std::fprintf(stderr, "%s: %s\n", msg, ZSTD_getErrorName(code));
std::exit(1);
}
int main() {
// 1. 准备原始数据(一段有重复内容的文本,压缩效果明显)
std::string text = "hello zstd hello zstd hello zstd, ";
text += "压缩速度与压缩比的黄金平衡,";
text += std::string(100, 'A'); // 100 个连续 'A',重复度高
const size_t srcSize = text.size();
// 2. 分配压缩缓冲区:ZSTD_compressBound 给出最坏情况下的压缩后大小
std::vector<char> compressed(ZSTD_compressBound(srcSize));
// 3. 压缩:默认级别 ZSTD_CLEVEL_DEFAULT(=3)
size_t cSize = ZSTD_compress(compressed.data(), compressed.size(),
text.data(), srcSize, ZSTD_CLEVEL_DEFAULT);
if (ZSTD_isError(cSize)) die("压缩失败", cSize);
std::printf("原始大小: %zu 字节\n", srcSize);
std::printf("压缩后大小: %zu 字节\n", cSize);
std::printf("压缩比: %.2fx\n", (double)srcSize / cSize);
// 4. 解压:先从压缩帧中读取原始大小(推荐做法)
unsigned long long const frameSize =
ZSTD_getFrameContentSize(compressed.data(), cSize);
if (frameSize == ZSTD_CONTENTSIZE_ERROR)
die("不是合法的 zstd 帧", 0);
if (frameSize == ZSTD_CONTENTSIZE_UNKNOWN)
die("帧大小未知(流式写入场景),本示例不会出现", 0);
std::vector<char> decompressed(frameSize);
// 5. 解压
size_t dSize = ZSTD_decompress(decompressed.data(), decompressed.size(),
compressed.data(), cSize);
if (ZSTD_isError(dSize)) die("解压失败", dSize);
// 6. 校验一致性
if (dSize != srcSize || std::memcmp(decompressed.data(), text.data(), srcSize) != 0) {
std::fprintf(stderr, "数据不一致!\n");
return 1;
}
std::printf("解压成功,内容与原始数据完全一致\n");
return 0;
}
编译运行(Linux/macOS):
bash
g++ -O2 simple_demo.cpp -o simple_demo -lzstd
./simple_demo
要点说明:
- ZSTD_compressBound(srcSize) 返回压缩后的最大可能大小,用它分配缓冲区最安全;
- ZSTD_getFrameContentSize() 在解压前读取原始长度,避免解压缓冲区开小了;
- 错误处理:所有 zstd API 返回 size_t,用 ZSTD_isError() 判断,用 ZSTD_getErrorName() 取错误文本,这是官方推荐的统一错误处理方式。
第 2 步:进阶参数与 Context 复用
如果你的程序要高频压缩大量小数据(比如每条网络消息都压缩),反复创建/释放压缩上下文会浪费性能。正确做法是创建一次 ZSTD_CCtx / ZSTD_DCtx,重复使用:
cpp
// context_demo.cpp ------ 复用 CCtx/DCtx + 参数精细控制
#include <zstd.h>
#include <cstdio>
#include <vector>
#include <string>
// 用 RAII 封装 zstd 上下文,自动释放,避免资源泄漏
struct CctxGuard {
ZSTD_CCtx* p;
CctxGuard() : p(ZSTD_createCCtx()) {}
~CctxGuard() { ZSTD_freeCCtx(p); }
};
struct DctxGuard {
ZSTD_DCtx* p;
DctxGuard() : p(ZSTD_createDCtx()) {}
~DctxGuard() { ZSTD_freeDCtx(p); }
};
int main() {
CctxGuard cctx;
DctxGuard dctx;
if (!cctx.p || !dctx.p) {
std::fprintf(stderr, "创建上下文失败\n");
return 1;
}
// 设置压缩级别为 9(更高压缩比,用于离线归档类场景)
ZSTD_CCtx_setParameter(cctx.p, ZSTD_c_compressionLevel, 9);
// 开启校验和:解压时能检测数据是否被篡改/损坏
ZSTD_CCtx_setParameter(cctx.p, ZSTD_c_checksumFlag, 1);
const std::string msg = "context reuse test: ";
for (int i = 0; i < 50; ++i) msg + "hello " + std::to_string(i) + " ";
// 用高级 API ZSTD_compress2 压缩(内部使用 cctx 的参数设置)
size_t const bound = ZSTD_compressBound(msg.size());
std::vector<char> out(bound);
size_t const cSize = ZSTD_compress2(cctx.p, out.data(), out.size(),
msg.data(), msg.size());
if (ZSTD_isError(cSize)) {
std::fprintf(stderr, "压缩失败: %s\n", ZSTD_getErrorName(cSize));
return 1;
}
// 解压
unsigned long long const srcSize = ZSTD_getFrameContentSize(out.data(), cSize);
std::vector<char> back(srcSize);
size_t const dSize = ZSTD_decompressDCtx(dctx.p, back.data(), back.size(),
out.data(), cSize);
if (ZSTD_isError(dSize)) {
std::fprintf(stderr, "解压失败: %s\n", ZSTD_getErrorName(dSize));
return 1;
}
std::printf("压缩 %zu -> %zu 字节(级别 9,带校验和),解压一致:%s\n",
msg.size(), cSize,
(dSize == msg.size() && std::string(back.data(), dSize) == msg) ? "是" : "否");
return 0;
}
关键参数速查(ZSTD_c_* 系列):
| 参数 | 作用 |
|---|---|
| ZSTD_c_compressionLevel | 压缩级别(-131072 ~ 22) |
| ZSTD_c_checksumFlag | 是否写入校验和(0/1) |
| ZSTD_c_nbWorkers | 并行压缩线程数(>1 启用多线程) |
| ZSTD_c_windowLog | 窗口大小对数(影响内存占用与压缩比) |
| ZSTD_c_forceMaxWindow | 允许 20~22 级别的超大窗口 |
第 3 步:流式压缩大文件(Streaming API)
文件往往很大(几个 GB),不能一次读进内存。流式 API 按块处理:读一块 → 压一块 → 写一块,内存占用恒定。
cpp
// file_stream_demo.cpp ------ 用流式 API 压缩/解压任意大小的文件
#include <zstd.h>
#include <cstdio>
#include <vector>
static void die(const char* msg, size_t code) {
std::fprintf(stderr, "%s: %s\n", msg, ZSTD_getErrorName(code));
std::exit(1);
}
// 压缩文件:inPath -> outPath
void compress_file(const char* inPath, const char* outPath, int level) {
FILE* fin = std::fopen(inPath, "rb");
FILE* fout = std::fopen(outPath, "wb");
if (!fin || !fout) { std::perror("打开文件失败"); std::exit(1); }
ZSTD_CCtx* cctx = ZSTD_createCCtx();
ZSTD_CCtx_setParameter(cctx, ZSTD_c_compressionLevel, level);
const size_t inBufSize = 64 * 1024; // 输入块 64KB
const size_t outBufSize = ZSTD_compressBound(inBufSize);
std::vector<char> inBuf(inBufSize), outBuf(outBufSize);
size_t lastRet = 0;
while (!std::feof(fin)) {
// 1. 读一块
size_t nRead = std::fread(inBuf.data(), 1, inBufSize, fin);
ZSTD_inBuffer input = { inBuf.data(), nRead, 0 };
bool lastChunk = (nRead < inBufSize); // 最后一块
// 2. 循环压缩这一块(一次可能输出多段,需循环取完)
while (input.pos < input.size) {
ZSTD_outBuffer output = { outBuf.data(), outBuf.size(), 0 };
// ZSTD_e_continue: 普通块;ZSTD_e_end: 最后一块,写完即结束帧
size_t const ret = ZSTD_compressStream2(cctx, &output, &input,
lastChunk ? ZSTD_e_end : ZSTD_e_continue);
if (ZSTD_isError(ret)) die("流式压缩失败", ret);
std::fwrite(outBuf.data(), 1, output.pos, fout);
if (lastChunk && ret == 0) break; // ret==0 表示帧已结束
}
lastRet = (nRead < inBufSize) ? 1 : 0;
if (lastRet) break;
}
std::fclose(fin); std::fclose(fout);
ZSTD_freeCCtx(cctx);
std::printf("压缩完成: %s -> %s\n", inPath, outPath);
}
// 解压文件:inPath -> outPath
void decompress_file(const char* inPath, const char* outPath) {
FILE* fin = std::fopen(inPath, "rb");
FILE* fout = std::fopen(outPath, "wb");
if (!fin || !fout) { std::perror("打开文件失败"); std::exit(1); }
ZSTD_DCtx* dctx = ZSTD_createDCtx();
const size_t inBufSize = 64 * 1024;
const size_t outBufSize = 256 * 1024; // 解压后可能膨胀,缓冲区开大些
std::vector<char> inBuf(inBufSize), outBuf(outBufSize);
ZSTD_inBuffer input = { inBuf.data(), 0, 0 };
while (true) {
// 输入缓冲消耗完就补充
if (input.pos == input.size) {
input.size = std::fread(inBuf.data(), 1, inBufSize, fin);
input.pos = 0;
if (input.size == 0) break; // 读完了
}
ZSTD_outBuffer output = { outBuf.data(), outBuf.size(), 0 };
size_t const ret = ZSTD_decompressStream(dctx, &output, &input);
if (ZSTD_isError(ret)) die("流式解压失败", ret);
std::fwrite(outBuf.data(), 1, output.pos, fout);
if (ret == 0) break; // ret==0 表示帧已完全解压
}
std::fclose(fin); std::fclose(fout);
ZSTD_freeDCtx(dctx);
std::printf("解压完成: %s -> %s\n", inPath, outPath);
}
int main(int argc, char** argv) {
if (argc < 2) { std::printf("用法: %s 输入文件\n", argv[0]); return 0; }
compress_file(argv[1], "out.zst", 3);
decompress_file("out.zst", "out.restored");
return 0;
}
流式 API 三个要点:
- ZSTD_inBuffer / ZSTD_outBuffer 都是 {ptr, size, pos} 三元组,pos 表示已消费/已产出位置;
- 一次输入可能产生多次输出,所以要内层 while 循环取到 input.pos == input.size 为止;
- 最后一个块用 ZSTD_e_end,返回值 0 表示帧结束;解压侧返回值 0 同样表示帧结束。
第 4 步:多线程压缩
大文件 + 多核 CPU = 多线程压缩。只需一行参数:
cpp
#include <zstd.h>
#include <cstdio>
int main() {
ZSTD_CCtx* cctx = ZSTD_createCCtx();
// 关键:设置并行 worker 数(4 线程)
ZSTD_CCtx_setParameter(cctx, ZSTD_c_nbWorkers, 4);
ZSTD_CCtx_setParameter(cctx, ZSTD_c_compressionLevel, 3);
// 之后用 ZSTD_compress2 / ZSTD_compressStream2 正常压缩即可,
// 大块数据会被自动切块并行处理
std::printf("已启用 4 线程并行压缩\n");
ZSTD_freeCCtx(cctx);
return 0;
}
⚠️ 多线程压缩只在数据块足够大时才有明显收益(建议单次 ≥ 1MB);小数据开多线程反而有线程调度开销。另外,多线程产出的 zstd 帧依然可以被普通解压器解压,兼容性无损。
第 5 步:字典压缩(小消息神器)
这是 zstd 最有特色、也最容易"真香"的功能。原理:先用一组同类型样本(比如 1000 条真实的 JSON 日志)训练出一个共享字典,之后每条小消息都带着字典压缩,重复字段直接被字典引用,体积骤降。
cpp
// dict_demo.cpp ------ 字典训练 + 字典压缩/解压
#include <zdict.h>
#include <zstd.h>
#include <cstdio>
#include <string>
#include <vector>
int main() {
// 1. 准备训练样本:模拟同类型的小 JSON 消息(字段名固定,值不同)
std::vector<std::string> samples;
for (int i = 0; i < 200; ++i) {
samples.push_back(
"{\"user_id\":" + std::to_string(1000 + i) +
",\"action\":\"click\",\"page\":\"/home\",\"ts\":" +
std::to_string(1700000000 + i * 10) + "}");
}
// 2. 训练字典:ZDICT_trainFromBuffer 从样本中提取高频片段
std::vector<char> dict(64 * 1024); // 字典缓冲区(典型值 8KB~64KB)
std::vector<size_t> sizes;
std::vector<const void*> ptrs;
for (auto& s : samples) { ptrs.push_back(s.data()); sizes.push_back(s.size()); }
size_t const dictSize = ZDICT_trainFromBuffer(
dict.data(), dict.size(), ptrs.data(), sizes.data(), (unsigned)sizes.size());
if (ZDICT_isError(dictSize)) {
std::fprintf(stderr, "训练字典失败: %s\n", ZDICT_getErrorName(dictSize));
return 1;
}
std::printf("字典训练完成,大小 %zu 字节\n", dictSize);
// 3. 用字典压缩一条新消息(不在训练集里,但结构相同)
const std::string msg = "{\"user_id\":9999,\"action\":\"click\",\"page\":\"/home\",\"ts\":1700009999}";
// 3.1 无字典压缩(对照组)
std::vector<char> plain(ZSTD_compressBound(msg.size()));
size_t plainSize = ZSTD_compress(plain.data(), plain.size(), msg.data(), msg.size(), 3);
// 3.2 有字典压缩
ZSTD_CCtx* cctx = ZSTD_createCCtx();
ZSTD_CDict* cdict = ZSTD_createCDict(dict.data(), dictSize, 3);
ZSTD_CCtx_refCDict(cctx, cdict);
std::vector<char> dicted(ZSTD_compressBound(msg.size()));
size_t dictedSize = ZSTD_compress2(cctx, dicted.data(), dicted.size(),
msg.data(), msg.size());
// 4. 用字典解压
ZSTD_DCtx* dctx = ZSTD_createDCtx();
ZSTD_DDict* ddict = ZSTD_createDDict(dict.data(), dictSize);
ZSTD_DCtx_refDDict(dctx, ddict);
unsigned long long const rawSize = ZSTD_getFrameContentSize(dicted.data(), dictedSize);
std::vector<char> back(rawSize);
ZSTD_decompressDCtx(dctx, back.data(), back.size(), dicted.data(), dictedSize);
std::printf("原始大小 : %zu 字节\n", msg.size());
std::printf("无字典压缩后 : %zu 字节\n", plainSize);
std::printf("带字典压缩后 : %zu 字节\n", dictedSize);
std::printf("解压内容一致 : %s\n",
(std::string(back.data(), back.size()) == msg) ? "是" : "否");
ZSTD_freeDDict(ddict);
ZSTD_freeDCtx(dctx);
ZSTD_freeCDict(cdict);
ZSTD_freeCCtx(cctx);
return 0;
}
输出示例(小 JSON 消息):
字典训练完成,大小 10240 字节
原始大小 : 67 字节
无字典压缩后 : 67 字节 <- 小数据无字典时几乎压不动
带字典压缩后 : 30 字节 <- 带字典后体积砍半以上
解压内容一致 : 是
字典使用的三个注意点:
- 字典要随压缩数据一起分发:解压方必须拿到同一份字典,否则解不出来;
- 训练样本要"像"真实数据:样本越接近线上数据,字典效果越好;样本不足时字典可能反而有害;
- 字典大小典型值 8KB~64KB:太小抓不住特征,太大会让每条小消息的字典开销变高。实际工程中常用 16KB~32KB。
六、与 zlib / LZ4 / brotli 对比
| 维度 | zstd | zlib (gzip) | LZ4 | brotli |
|---|---|---|---|---|
| 压缩速度 | 快 | 慢 | 极快 | 慢 |
| 解压速度 | 极快 | 慢 | 极快 | 中 |
| 压缩比 | 高(默认级别≈zlib 或更高) | 中 | 低 | 最高(高等级下) |
| 字典支持 | ✅ 内置,可训练 | ❌ 无 | ✅(有受限支持) | ❌ 无 |
| 多线程压缩 | ✅ 官方参数 | ❌ 需自行实现 | ✅ | ❌ |
| 流式 API | ✅ 完善 | ✅ | ✅ | ✅ |
| 许可证 | BSD | zlib | BSD | MIT |
| 典型场景 | 数据库/日志/传输/打包 | 历史兼容/HTTP | 实时缓存/内存交换 | Web 静态资源 |
选型建议:
- 需要"压缩比不错 + 速度快"的通用场景 → zstd,没有悬念;
- 只追求极限速度、不在乎体积 → LZ4;
- 必须兼容老系统/老接口(gzip 格式约定)→ zlib;
- 纯 Web 静态资源、追求极限压缩比且可接受慢压缩 → brotli。
七、常见坑点与避坑指南
⚠️ 坑 1:压缩缓冲区开小了 一定要用 ZSTD_compressBound(srcSize) 来分配压缩输出缓冲区。它给的是最坏情况大小(某些不可压缩数据压缩后反而略大),不要凭"经验值"拍脑袋。
⚠️ 坑 2:解压缓冲区开小了 优先用 ZSTD_getFrameContentSize() 读取帧内原始大小;对未知大小的流(ZSTD_CONTENTSIZE_UNKNOWN)要用流式解压 API 配合动态扩容,而不是一次性解压。
⚠️ 坑 3:忘记处理 ZSTD_isError zstd 用返回值携带错误码,size_t 可能看起来像"很大的正常值"。所有 API 调用后都必须用 ZSTD_isError() 检查,否则内存越界、数据损坏都是静默的。
⚠️ 坑 4:字典不匹配 压缩用了字典 A,解压用了字典 B(或没带字典)→ 直接解压失败。字典必须与数据一起分发、版本管理。
⚠️ 坑 5:级别 20+ 不生效 库中 ZSTD_maxCLevel() 返回 22,但 20~22 需要同时设置 ZSTD_c_forceMaxWindow 才能用(超大窗口),否则 API 会拒绝。CLI 中对应 --ultra 选项。
⚠️ 坑 6:把 zstd 帧当 gzip 用 zstd 的帧格式和 gzip 完全不同,.zst 文件不能直接用 gzip 解。跨系统分发时确认对方用的是 zstd 工具链(zstd -d)。
⚠️ 坑 7:多线程压缩的小数据反优化 ZSTD_c_nbWorkers 只对足够大的数据有收益。每条几十 KB 的小消息建议保持单线程,避免线程池创建/唤醒的开销。
八、FAQ 速查表
| 问题 | 答案 |
|---|---|
| zstd 是什么许可证? | BSD,可商用、可修改,无需开源你的代码 |
| 最新版本怎么查? | GitHub 仓库 facebook/zstd releases 页面 |
| 压缩级别范围是多少? | ZSTD_minCLevel()=-131072 到 ZSTD_maxCLevel()=22,默认 3 |
| 如何判断 API 返回是否出错? | ZSTD_isError(ret),再用 ZSTD_getErrorName(ret) 取文本 |
| 解压前怎么知道原始大小? | ZSTD_getFrameContentSize();未知则用流式解压 |
| 多线程怎么开? | ZSTD_CCtx_setParameter(cctx, ZSTD_c_nbWorkers, n) |
| 小消息压不动怎么办? | 用 ZDICT_trainFromBuffer 训练字典,配合 ZSTD_CDict 使用 |
| 压缩后的数据能跨版本解压吗? | 能,zstd 帧格式向后兼容,老版本也能解新版本(同级别窗口内)压的帧 |
| CLI 怎么用? | 压缩:zstd file;解压:zstd -d file.zst;指定级别:zstd -9 file |
| C++ 有官方封装吗? | 官方核心是 C API;社区封装有 cppzstd、zstdpp 等,也可用 RAII 自行封装 |
| 和 gzip 相比谁更小? | 同级别下 zstd 压缩比与 zlib 相当或略优,且快得多 |
| 嵌入式能用吗? | 能,核心库无第三方依赖,可编译成静态库,内存占用可调 |
小结
zstd 是目前 C++ 生态里"性价比"最高的通用压缩方案:速度接近 LZ4、压缩比接近 zlib、自带字典与多线程能力,且 API 设计清晰、文档完善、生产验证充分。无论你是做日志归档、网络传输、游戏资源打包,还是给数据库选压缩算法,zstd 都值得作为第一候选。
建议的下一步练习:
- 用 Simple API 写一个"内存消息压缩器",接入你的网络收发模块;
- 用 Streaming API 给项目加一个日志文件归档压缩功能;
- 收集 1000 条真实业务消息,训练字典并对比压缩率,感受"小消息神器"的威力。