压缩库的结构、构建与集成指南)
TDengine 中 Zstandardzstd压缩库的结构、构建与集成指南【免费下载链接】TDengineHigh-performance, scalable time-series database designed for Industrial IoT (IIoT) scenarios项目地址: https://gitcode.com/GitHub_Trending/tde/TDengine本文以 TDengine 仓库内 contrib/TSZ/zstd 下的 Zstandard 库为线索系统讲解 libzstd 的目录组织、构建方式、稳定与实验 API、模块化裁剪、多线程与旧格式兼容等核心话题并结合 source/util/src/tcompression.c 等源码说明该库如何在 TDengine 的时间序列数据压缩链路中落地。读完本文你将掌握 zstd 源码目录的模块划分逻辑、Makefile 与 CMake 两种构建路径、压缩/解压 API 的选择策略以及如何针对嵌入式与时序场景裁剪与配置 zstd。Zstandard 与它在 TDengine 中的角色Zstandard简称 zstd是一种面向实时压缩场景的快速无损压缩算法目标是在 zlib 级别的速度下提供更高的压缩比。zstd.h 的引言部分明确指出库支持从 1 到ZSTD_maxCLevel()当前为 22的压缩级别其中 20--ultra级别需要更多内存、应谨慎使用。在本仓库中zstd 被作为“二级压缩L2 compression”算法之一随源码静态编译进 TDengine。contrib/TSZ目录同时托管了 TSZ 压缩算法sz/与 zstd 库本身二者共同构成 contrib/TSZ/CMakeLists.txt 中定义的TSZ静态库AUX_SOURCE_DIRECTORY(sz/src SRC1) AUX_SOURCE_DIRECTORY(zstd/dictBuilder SRC2) AUX_SOURCE_DIRECTORY(zstd/common SRC3) AUX_SOURCE_DIRECTORY(zstd/compress SRC4) AUX_SOURCE_DIRECTORY(zstd/decompress SRC5) AUX_SOURCE_DIRECTORY(zstd/deprecated SRC6) AUX_SOURCE_DIRECTORY(zstd/legacy SRC7) ADD_LIBRARY(TSZ STATIC ${SRC1} ${SRC2} ${SRC3} ${SRC4} ${SRC5} ${SRC6} ${SRC7}) TARGET_INCLUDE_DIRECTORIES(TSZ PUBLIC sz/inc zstd zstd/common ${TD_SOURCE_DIR}/include)可以看到zstd 的 6 个功能子目录common、compress、decompress、deprecated、legacy、dictBuilder被整体编入TSZ静态库这也与 README 中“lib 目录按功能拆分、便于取舍”的设计一一对应。而 TDengine 主库通过 source/util/src/tcompression.c 中的compressL2Dict见 tcompression.c把zstd调度到l2ComressInitImpl_zstd / l2CompressImpl_zstd / l2DecompressImpl_zstd从而把 zstd 接入到列式压缩管线中。仓库内的 zstd 版本与文件构成contrib/TSZ/zstd 是一个完整的 libzstd 源码快照顶层结构如下路径内容common/所有变体都必需的公共基础代码内存、位流、熵编码FSE/HUF、线程池等compress/压缩实现zstd_compress.c及 fast / double-fast / lazy / opt / LDM 等策略模块decompress/解压实现zstd_decompress.c、huf_decompress.cdictBuilder/基于样本集合生成字典zdict.h、cover.c、divsufsort.clegacy/v0.1.0 起的旧格式解码支持zstd_v01.c~zstd_v07.cdeprecated/正在淘汰的旧 APIzbuff.hzstd.h稳定 API 头文件LICENSE/Makefile/README.md许可证BSD、构建脚本与本文档关于版本有一个值得注意的细节README 中所述“Starting v0.8.0, all versions of zstd produce frames compliant with specification”与仓库内legacy/目录仅收录到zstd_v07的事实相互印证——旧格式兼容最多需要回溯到 v0.1.0而 v0.8.0 之后产出的帧已完全符合规范不再需要 legacy 模块。本快照的基础版本为 1.3.5并在 zstd.h 中注明已回移植 CVE-2019-11922 修复在ZSTD_buildCTable写输出缓冲区前增加边界检查因而编译出的库同样具备该安全修复。构建 libzstdMakefile 标准目标与 CMake 集成使用自带 Makefile 构建仓库随库提供了遵循 GNU Makefile 约定的 Makefile支持命令变量、分阶段安装、目录变量与标准 targetmake # 同时生成静态库与动态库 make install # 安装到系统默认目录 make lib-mt # 生成多线程版本自动定义 ZSTD_MULTITHREAD 并启用 -pthread make libzstd # Windows 下用 MinGWMSYS 生成 dll\libzstd.dll 与导入库 dll\libzstd.lib默认libzstd的功能范围包括压缩、解压、字典构建以及 v0.4.0旧格式的解码支持legacy 完整能力见下文“模块化构建”。在 TDengine 中通过 CMake 静态集成TDengine 不直接使用上面的 Makefile而是由 contrib/TSZ/CMakeLists.txt 通过AUX_SOURCE_DIRECTORY把zstd下各子目录的源文件全部编入TSZ静态库并公开zstd与zstd/common两个头文件搜索路径。这样上层代码只需#include zstd.h即可使用全部功能同时避免了依赖系统动态库带来的版本漂移。若只需压缩或只需解压能力可参照下文“模块化构建”在编译时通过宏裁剪进一步缩小二进制体积。API 分层稳定 API 与高级 API稳定 APISimple API稳定 API 集中在 zstd.h核心是两组一次调用即完成压缩/解压的函数ZSTD_compress(dst, dstCapacity, src, srcSize, compressionLevel)将src整体压缩为单个 zstd 帧写入dst若dstCapacity ZSTD_compressBound(srcSize)可获得更快路径返回写入字节数或错误码用ZSTD_isError()判断。ZSTD_decompress(dst, dstCapacity, src, compressedSize)compressedSize必须是压缩帧或 skippable 帧的精确大小dstCapacity是原始大小的上界返回解压字节数或错误码。若无法预知原始大小上界应改用流式接口。README 也提到zstd 的压缩可以在三种模式下进行单步Simple API、复用上下文的单步Explicit context、任意多步Streaming compression小数据的压缩比可通过字典得到显著提升Simple dictionary API / Bulk-processing dictionary API。错误处理与实验 APIcommon/zstd_errors.h把size_t形式的函数返回值翻译成ZSTD_ErrorCode枚举便于精确错误处理其被 common/error_private.h 引用。ZSTD_STATIC_LINKING_ONLY在#include zstd.h之前定义该宏可以解锁zstd.h第二部分中的实验性 API。这些 API 不稳定定义可能随时变化绝不能与动态库一起使用只允许静态链接——TDengine 通过 CMake 静态编译 zstd 恰好满足这一前提。多线程能力通过实验 APIZSTD_compress_generic()见 zstd.h暴露该 API 目前仍被标记为实验性但未来预期会转正。已废弃 APIlib/deprecated目录存放着正在被淘汰的旧接口目前主要是较老的流式原型zbuff.h含zbuff_common.c、zbuff_compress.c、zbuff_decompress.c。README 明确建议迁移到 zstd.h 中受支持的流式 API这些旧原型将在未来版本中移除。TDengine 之所以仍将deprecated目录编入TSZ库见 contrib/TSZ/CMakeLists.txt主要是为了保持上游源码完整性业务代码并不依赖zbuff接口。模块化构建按需裁剪功能README 给出了一套清晰的模块依赖关系lib/common是所有变体都必需的压缩源码在lib/compress解压源码在lib/decompress二者互不依赖可以只编压缩或只编解压lib/dictBuilder从一组样本生成字典API 见 dictBuilder/zdict.h依赖commoncompresslib/legacy解码 v0.1.0 起的旧 zstd 格式依赖commondecompress只支持解码且需要定义ZSTD_LEGACY_SUPPORT才会启用典型用法gcc-DZSTD_LEGACY_SUPPORT1数值越大支持的版本范围越窄ZSTD_LEGACY_SUPPORT2表示“支持 v0.2.0 的旧格式”3表示 v0.3.0依此类推由于 v0.8.0 起所有版本产出的帧都符合规范ZSTD_LEGACY_SUPPORT8或更大实际上不会触发任何 legacy 支持ZSTD_LEGACY_SUPPORT0表示“不支持旧格式”一旦启用解压函数会自动透明地处理旧格式也可直接调用 legacy/zstd_legacy.h 暴露的旧 API例如 v0.4 的高级 API 在 legacy/zstd_v04.h。与之配套的还有四个“开关宏”置 0 即跳过对应特性的编译并会连带禁用其依赖例如ZSTD_LIB_COMPRESSION0会同时禁用 dictBuilder-DZSTD_LIB_COMPRESSION0 # 不编译压缩 -DZSTD_LIB_DECOMPRESSION0 # 不编译解压 -DZSTD_LIB_DICTBUILDER0 # 不编译字典构建 -DZSTD_LIB_DEPRECATED0 # 不编译废弃 API从仓库源码看legacy模块由 common/zstd_internal.h 与decompress链路共同支撑而compress/内部的策略模块compress/zstd_fast.c、compress/zstd_double_fast.c、compress/zstd_lazy.c、compress/zstd_opt.c、compress/zstd_ldm.c分别对应不同的速度/压缩比档位由zstd_compress.c根据参数分派。多线程支持README 明确指出用make默认构建时多线程是关闭的。启用需要同时满足两个条件定义宏ZSTD_MULTITHREADPOSIX 系统上以 pthread 编译gcc 加-pthread。这两个条件会被make lib-mttarget 自动触发。另外在链接一个使用了多线程版 libzstd 的 POSIX 程序时链接阶段同样需要-pthread标志否则可能出现未定义符号。仓库中与多线程直接相关的文件包括 common/threading.c、common/threading.h注释标明其源头为 zstdmt 仓库、common/pool.c线程池实现依赖ZSTD_malloc/ZSTD_free以及 compress/zstdmt_compress.c。其中zstdmt_compress.c的接口会被 compress/zstd_compress_internal.h 条件引入说明多线程压缩是压缩链路中的一个可选挂载点。Windows 平台MinGWMSYS 生成 DLL在 Windows 上可以使用 MinGWMSYS 执行make libzstd来生成 DLLmake libzstd该命令产生dll\libzstd.dll与导入库dll\libzstd.lib。使用要点导入库dll\libzstd.lib仅在 Visual C 场景下必需用 gcc/MinGW 编译项目只需zstd.h头文件 dll\libzstd.dll动态库并在链接选项中加上该动态库例如一个只包含test-dll.c的项目可这样编译gcc $(CFLAGS) -Iinclude/ test-dll.c -o test-dll dll\libzstd.dll编译出的可执行文件运行时需要dll\libzstd.dll。注意这与 TDengine 在 Linux 上的集成方式不同TDengine 仓库本身通过 CMake 以静态库形式使用 zstd而 Makefile 的 DLL 路径适合独立的 Windows 集成场景。深入 TDengine 集成zstd 在时序压缩链路中的实际用法二级压缩调度表在 source/util/src/tcompression.c 中TDengine 定义了二级压缩L2的级别映射与函数表TCmprLvlSet compressL2LevelDict[] { {unknown, .lvl {1, 2, 3}}, {lz4, .lvl {1, 2, 3}}, {zlib, .lvl {1, 6, 9}}, {zstd, .lvl {1, 11, 22}}, {tsz, .lvl {1, 2, 3}}, {xz, .lvl {1, 6, 9}}, };zstd 在低/中/高三档压缩级别上分别映射到 1 / 11 / 22其中 22 属于需要更多内存的--ultra档位正好呼应 zstd.h 中对高级别用内存的警告。在非 Windows、非 macOS 平台上zstd被绑定到真正的 zstd 实现l2ComressInitImpl_zstd / l2CompressImpl_zstd / l2DecompressImpl_zstd而在 Windows 与 macOS 上则回退为 lz4 实现见 tcompression.c因此 zstd 完整能力目前面向 Linux 等 POSIX 平台。zstd 作为二级压缩器的实现细节source/util/src/tcompression.c 中的l2CompressImpl_zstd展示了 zstd 在块级压缩中的典型封装int32_t l2CompressImpl_zstd(const char *const input, const int32_t inputSize, char *const output, int32_t outputSize, const char type, int8_t lvl) { size_t len ZSTD_compress(output 1, outputSize - 1, input, inputSize, lvl); if (len inputSize) { output[0] 0; memcpy(output 1, input, inputSize); return inputSize 1; } output[0] 1; return len 1; }要点解读首字节output[0]作为标志位0表示“压缩未带来收益、存原始数据”1表示“后续为 zstd 帧”解压端l2DecompressImpl_zstd据此分发input[0]1时调用ZSTD_decompress(output, outputSize, input 1, compressedSize - 1)input[0]0时直接拷贝非法标志返回TSDB_CODE_THIRDPARTY_ERROR压缩级别lvl来自tsGetCompressL2Level()对compressL2LevelDict的查表低/中/高分别为 1/11/22数据先经过 TDengine 第一级L1的列式编码如 PLAIN、SIMPLE-8B、DELTAI、BIT-PACKING、DELTAD、BYTE-STREAM_SPLIT见 tcompression.c再进入 zstd 等 L2 算法做字节级压缩形成“时序编码 通用压缩”的两级管线。此外source/util/src/tcompression.c 中的zstdCompressImpl / zstdDecompressImpl是另一处封装固定使用级别 9并约束“压缩后大于原始大小即视为失败”len srcSize返回 -1与块级封装中“回退存原样”的策略略有不同适用于不允许膨胀的其他调用方。可选的加速库注入source/util/src/tcompression_accel.c 提供了把 zstd 等压缩库替换为外部加速实现如 arm64 优化版 libzstd的机制构建时 zlib/zstd/lz4 以静态方式链入 util同时把TAOS_COMPRESS_ACCEL{,_ZLIB,_ZSTD,_LZ4}等符号指向 ABI 兼容的替代实现启动时通过 dlopen 解析。对于 zstd加速路径通过ZSTD_compress/ZSTD_decompress这两个稳定 API 符号完成装载并打补丁到compressL2Dict[L2_ZSTD]见 tcompression_accel.c环境变量TAOS_COMPRESS_ACCEL_ZSTD可用于指定具体动态库路径。这也再次说明在 TDengine 的集成中zstd 只需要使用稳定 API 即可获得完整能力而ZSTD_STATIC_LINKING_ONLY实验 API 仅适合需要更精细控制的静态链接场景。TSZ 算法中的 zstd 帧识别contrib/TSZ/sz/src/utility.c 展示了在判断压缩器类型时如何利用 zstd 的元数据 API通过ZSTD_getFrameContentSize()版本号 1.3.0 时或ZSTD_getDecompressedSize()探测压缩块是否为 zstd 帧返回ZSTD_COMPRESSOR判定结果压缩时使用默认级别 3即ZSTD_CLEVEL_DEFAULT这与 zstd.h 中定义的默认压缩级别常量一致。常见构建与集成问题速查问题原因与解决办法链接报 zstd 未定义符号确认是否启用了ZSTD_STATIC_LINKING_ONLY实验 API 却链接了动态库——实验 API 禁止用于动态库请改用静态链接多线程程序链接失败make默认关闭多线程请用make lib-mt构建并在编译与链接阶段都加-pthread同时确认定义了ZSTD_MULTITHREAD希望减小体积利用ZSTD_LIB_COMPRESSION/ZSTD_LIB_DECOMPRESSION/ZSTD_LIB_DICTBUILDER/ZSTD_LIB_DEPRECATED四个宏裁剪功能只保留common是底线是否支持旧格式需要-DZSTD_LEGACY_SUPPORTNN 为最低支持的旧版本号如 1N8无实际作用N0表示不支持legacy 只提供解码能力压缩后体积反而变大这是正常现象TDengine 的块级封装l2CompressImpl_zstd检测到len inputSize时会回退为存原始数据并置标志位 0独立封装zstdCompressImpl则直接返回错误Windows 集成用make libzstd生成dll\libzstd.dllgcc/MinGW 场景只需头文件加 DLL 参与链接Visual C 场景还需导入库dll\libzstd.lib小结zstd 是一个“速度快、压缩比高、功能可按需裁剪”的无损压缩库其在 TDengine 中的角色是二级压缩算法之一。本仓库给出了它的完整源码与两条构建路径上游 Makefile支持标准 target、多线程与 DLL 生成和 TDengine 的 CMake 静态集成编入TSZ库。通过稳定 APIZSTD_compress/ZSTD_decompress、错误码枚举、ZSTD_STATIC_LINKING_ONLY实验宏、ZSTD_LEGACY_SUPPORT兼容开关以及make lib-mt多线程能力读者可以根据自己的场景纯压缩、纯解压、字典、旧格式、多线程灵活组合再结合 source/util/src/tcompression.c 中的调度表与两级压缩管线即可理解 zstd 如何在 TDengine 的时序数据存储路径中真正生效。【免费下载链接】TDengineHigh-performance, scalable time-series database designed for Industrial IoT (IIoT) scenarios项目地址: https://gitcode.com/GitHub_Trending/tde/TDengine创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考