TDengine 数据压缩机制深度解析存储压缩、有损压缩与传输压缩的实现原理【免费下载链接】TDengineHigh-performance, scalable time-series database designed for Industrial IoT (IIoT) scenarios项目地址: https://gitcode.com/GitHub_Trending/tde/TDengineTDengine 作为面向工业物联网IIoT的高性能时序数据库在数据的存储与传输两个环节都内建了压缩技术用以降低存储成本、减少网络带宽消耗。本文基于官方文档 数据压缩 展开并结合 压缩核心实现、压缩接口定义、运行时加速压缩模块 与 compressBench 微基准工具 等源码完整讲清 TDengine 的两级存储压缩体系含各数据类型的一级编码算法、可插拔硬件加速二级压缩、TSZ 有损压缩以及compressMsgSize传输压缩配置帮助读者在理解压缩原理的同时掌握编译开关、环境变量与配置参数的实操方法。为什么时序数据适合高压缩比TDengine 在存储架构上采用列式存储在存储介质中数据以列为单位连续存储这与以行为单位的行式存储不同。列式存储与时序数据的特性天然契合——同一列的值类型相同、变化平稳是压缩算法最愿意“讨好”的输入。为进一步挖掘冗余TDengine 还采用差值编码不直接存储原始值而是存储相邻数据点之间的差异。对平稳变化的时序序列如温度、水位、转速差值往往远小于原始值信息量大幅缩减。差值编码之后再叠加通用压缩算法做二次压缩两级配合达成高压缩率。对设备采集的稳定时序数据压缩后体积通常可控制在原始数据的 10% 以内。从源码结构看这套“差值/重编码 通用压缩”的两段式设计落实在 source/util/src/tcompression.c 中文件头部的算法说明注释明确给出了各类型的编码策略且压缩流程通过一张“算法字典表”compressL1Dict[]/compressL2Dict[]统一分发为后文的可插拔硬件加速能力奠定了基础。压缩标记的位域编码include/util/tcompression.h 定义了两套压缩标记编码是理解整个压缩体系的关键32 位标记格式高 8 位为一级压缩算法L1、中间 16 位为二级压缩算法L2、低 8 位为压缩等级level8 位标记格式compressMsgSize相关的旧版标志低 3 位 L1、中 3 位 L2、高 2 位 level。此外头文件还定义了压缩模式常量NO_COMPRESSION 0、ONE_STAGE_COMP 1、TWO_STAGE_COMP 2以及 zigzag 编解码宏 ZIGZAG_ENCODE / ZIGZAG_DECODE。存储层在 tdataformat.c 中依据这些模式分派TWO_STAGE_COMP时先做 L1 再走 L2且非压缩数据需要额外预留COMP_OVERFLOW_BYTES2 字节标记空间。一级压缩按数据类型定制的重编码时序数据自设备采集后遵循 TDengine 的数据建模规则每台采集设备构建为一张子表该设备产生的所有时序数据记录在同一张子表中。数据存储以块SMA/数据块为单位分块进行每个数据块只包含一张子表的数据压缩同样以块为单位对子表中的每一列分别压缩压缩后的数据仍按块落盘。时序数据的平稳性是其主要特征之一——采集的大气温度、水温等通常在一定范围内波动。利用这一特性TDengine 按数据类型选择编码方式。各类型的一级压缩策略与源码实现一一对应数据类型编码策略源码实现时间戳只记录相邻时间点的差值采集频率固定、差值小DELTAItsCompressTimestampImp2布尔位打包1 bit 表示一个布尔值1 字节存 8 个另有 RLE 模式BIT-PACKINGtsCompressBoolImp2数值tinyint/smallint/int/bigint差值 zigzag 编码 simple 8B 变长编码SIMPLE-8BtsCompressINTImp2浮点 float/doubledelta-deltaXOR 相邻值按前导零/尾随零数量选择存储字节方向DELTADtsCompressFloat/DoubleImp2字符串字典/字典压缩思想用短标识替换高频长串tsCompressString*系列L1 算法字典在 tcompression.c 第 285-289 行 中登记为五个具名编码器{PLAIN, NULL, tsCompressPlain2, tsDecompressPlain2}, {SIMPLE-8B, NULL, tsCompressINTImp2, tsDecompressINTImp2}, {DELTAI, NULL, tsCompressTimestampImp2, tsDecompressTimestampImp2}, {BIT-PACKING, NULL, tsCompressBoolImp2, tsDecompressBoolImp2}, {DELTAD, NULL, tsCompressDoubleImp2, tsDecompressDoubleImp2},这与 include/util/tcompression.h 中TCmprL1Type枚举L1_SIMPLE_8B、L1_XOR、L1_RLE、L1_DELTAD、L1_BSS等共同构成一级压缩的分发体系。几个值得注意的实现细节整数与时间戳先算差值再用 zigzag 编码把有符号差值映射为无符号值把补码最高位移到低位负数除符号位外其余位取反使有效数据位集中、前导零增多随后用 simple 8B 变长编码每段数据用 selector 声明“每值占用位数”如 {0, 0, 1, 2, 3, 4, 5, 6, 7, 8, 10, 12, 15, 20, 30, 60} bit。tcompression.c 的算法注释 还指出一个边界bigint 实际只用 59 位允许的数据范围为 -(2^59) 到 (2^59)-1。布尔提供两种方法——直接 1 bit 编码压缩率 1/8以及适合连续同值较多的 RLE 游程编码。浮点数采用与 Akumuli 相同的 delta-delta 方法对相邻浮点值做 XOR 后比较前导零与尾随零的数量若前导零更多则记录 XOR 结果的末尾若干字节否则记录开头的对应字节。这一技巧依赖浮点数“小数部分变化小、高位稳定”的平稳特性。字符串源码注释注明字符串一级压缩走 LZ4见 tcompression.c 第 38-39 行同时保留了字典式压缩的接口。此外tcompression.h 还暴露了tsDecompressFloatImpAvx2、tsDecompressDoubleImpAvx2、tsDecodeDoubleBssAvx2、tsDecompressTimestampAvx512等 SIMD 加速解码入口说明解压路径同样做了向量化优化实现见 tdecompressavx.c。二级压缩通用压缩算法与等级选择一级压缩专注于利用数据类型特性的“局部精简”之后TDengine 再把结果视为无差别的二进制数据用通用压缩算法做二次压缩。二级压缩的侧重点在于消除数据块内部编码后仍残留的信息冗余——例如大量相同的游程、重复的零字节。两级相辅相成共同实现高压缩率。从源码看二级压缩的分发表 compressL2Dict[] 登记了五种 codeclz4高吞吐、低延迟压缩率较低zlib经典的 deflate 压缩zstd可配等级压缩率与速度均衡性好tsz浮点有损压缩下文详述xz基于 fast-lzma2 实现的更高压缩率方案。每个 codec 支持低/中/高三档等级等级映射表 compressL2LevelDict 给出了具体取值例如 zstd 的三档对应 level 1 / 11 / 22xz 对应 1 / 6 / 9。用户可以在压缩率与写入速度之间按场景权衡选择。压缩与解压的实际调用路径位于 tcompression.c 的压缩分派宏按 L1 类型解码/编码后若为TWO_STAGE_COMP再经compressL2Dict[l2].comprFn执行二级压缩并输出 trace 日志记录所用算法与等级。使用硬件加速的二级压缩库可选二级压缩默认链接打包在 TDengine 中的静态 zlib/zstd/lz4。如果部署环境提供了硬件加速的 ABI 兼容替代品例如 Intel QAT/IAA 加速版 zlib、ISA-L 的 libz、ARM 上经过 SVE 优化的 zstd 等可以让 taosd 在启动时把这些替代品挂入二级压缩 dispatch table从而在不修改 SQL 的前提下获得加速如果替代品不可用自动回退到打包的静态实现。该能力仅在 Linux 平台、并且编译时显式开启BUILD_WITH_ACCEL_COMPRESS时生效开关定义见 cmake/options.cmake默认 OFF。编译方式# 编译 mkdir build cd build cmake -DBUILD_WITH_ACCEL_COMPRESSON .. make -j$(nproc)启动时通过环境变量告诉 taosd 从哪里加载替代库环境变量取值说明TAOS_COMPRESS_ACCEL目录路径 / 不设置设为目录则按惯例从dir/libz.so、dir/libzstd.so、dir/liblz4.so加载不设置或设为空时使用内置实现TAOS_COMPRESS_ACCEL_ZLIB.so完整路径单独覆盖 zlib 路径优先于TAOS_COMPRESS_ACCELTAOS_COMPRESS_ACCEL_ZSTD.so完整路径同上覆盖 zstdTAOS_COMPRESS_ACCEL_LZ4.so完整路径同上覆盖 lz4这一机制的完整实现在 source/util/src/tcompression_accel.c启动时tcompressionAccelInit()在 tsCompressInit 内被无条件调用未开启编译开关时该文件编译为 no-op 桩调用方无需特判随后对每个 codec 依次dlopen动态库、dlsym取出函数指针最后把compressL2Dict[L2_ZLIB/ZSTD/LZ4].comprFn/decomprFn替换为加速版包装函数如 accelCompress_zlib。符号约定替代库必须导出与上游一致的公共符号TDengine 启动时会dlsym它们libzcompress2、uncompresslibzstdZSTD_compress、ZSTD_decompressliblz4LZ4_compress_default、LZ4_decompress_safeABI 必须与上游相同参数顺序、返回值语义。绝大多数硬件加速版本都是 drop-in 替换无需关心。失败回退任一步失败环境变量未设、文件不存在、dlopen失败、缺符号都不会中断 taosd 启动该 codec 继续使用静态打包的实现并在日志中输出UTL WARN accel codec: ...对应源码中的 uWarn 回退分支。确认加载成功taosd 启动日志会显示一行类似UTL INFO accel zlib: loaded from /opt/qat-zlib/libz.so, L2_ZLIB dispatch patched UTL INFO accel zstd: loaded from /opt/qat-zlib/libzstd.so, L2_ZSTD dispatch patched如果完全没设环境变量则会看到UTL INFO accel compression: TAOS_COMPRESS_ACCEL{,_ZLIB,_ZSTD,_LZ4} unset; using stock L2 implementations评估加速效果与BUILD_TOOLSON一起编译会得到compressBench可以直接对二级压缩 dispatch table 做微基准绕开 SQL、网络、WAL结果只反映压缩本身# Stock 基线不设 TAOS_COMPRESS_ACCEL ./build/bin/compressBench --codec all --size 1 --iters 30 --warmup 5 \ --shape mixed --label stock --csv result.csv # 切换到加速库再跑一次对比同 codec 的 throughput export TAOS_COMPRESS_ACCEL/opt/qat-zlib ./build/bin/compressBench --codec all --size 1 --iters 30 --warmup 5 \ --shape mixed --label accel --csv result.csvcompressBench.c 的实现对这些参数给出了准确含义工具直接调用compressL2Dict的函数指针进行压/解压计时因此测得的就是 taosd 运行时实际使用的那份 dispatch tablestock 或 accel--shape提供random / repeating / sequential / mixed四种数据形态随机高熵数据、64 字节重复模式、单调递增的 1 毫秒间隔纳秒时间戳、以及三者等量混合可分别评估高熵、低熵、单调时间戳和混合负载下的表现--size支持 0.0044 KiB、0.062564 KiB、11 MiB等 MiB 计值覆盖真实列块的常见尺寸输出每个 codec 的 mean / p50 / p95 / stdev 以及 MB/s 吞吐和压缩率CSV 行尾标注backendstock|accel工具通过比较函数指针是否等于静态实现来判定后端以便对照。建议至少跑 30 轮 measure 5 轮 warmup并在不同空闲时段重复两次以排除瞬时干扰。注意事项替代库会被dlopen一次并常驻整个 taosd 生命周期因此磁盘上别在运行期间替换或删除该文件如果替代库本身又依赖其他动态库例如 QAT 用户态驱动需要确保它们也能被dlopen找到——常规手段/etc/ld.so.conf.d/、LD_LIBRARY_PATH都适用TSZ浮点有损压缩和 XZ 不在替换范围内前者是 TDengine 内部实现后者使用的是 fast-lzma2 而非主流 xz没有通用的 drop-in 加速版。有损压缩TSZTDengine 引擎为浮点数类型数据提供了无损压缩和有损压缩两种模式。浮点数的精度通常由其小数点后的位数决定。在某些情况下设备采集的浮点数精度较高但实际应用中关注的精度却较低此时采用有损压缩可以有效地节约存储空间。TSZTDengine SZ的算法基于预测模型核心思想是利用前序数据点的趋势来预测后续数据点的走势按用户声明的误差区间量化编码压缩率显著高于无损模式。相关实现位于 contrib/TSZ含sz/与zstd/子模块及 CMakeLists.txt引擎侧入口为 tcompression.h 中的 TSZ 接口tsCompressInit(char *lossyColumns, float fPrecision, double dPrecision, uint32_t maxIntervals, uint32_t intervals, int32_t ifAdtFse, const char *compressor)按列名启用 float/double 有损模式并传入误差精度、最大区间数等参数内部调用tdszInit见 tsCompressInit 实现tsCompressFloatLossyImp / tsCompressDoubleLossyImp及其解压函数有损编解码的具体入口二级压缩字典中tsz独立占一个 slotcompressL2Dict 中{tsz, ...}等级映射为 1 / 2 / 3与无损 L2 并列说明有损压缩也是以“二级压缩”的形式挂入同一套分发框架的。启用有损压缩需要用户在建模/建库时显式指定哪些浮点列允许有损及精度参数对精度敏感的场景应坚持默认的无损模式。传输压缩compressMsgSize 与 REST/WebSocketTDengine 在数据传输过程中同样提供压缩以减少网络带宽消耗。使用原生连接如 taosc向服务器传输数据时可通过配置文件 taos.cfg 中的compressMsgSize选项开启压缩传输可配置的值含义如下0所有数据包都压缩原文档表述为“对所有数据包进行压缩”-1禁用压缩传输其他正值仅对大于该阈值字节的消息体压缩。配置项在 packaging/cfg/taos.cfg 中默认注释为# compressMsgSize -1服务端解析逻辑见 source/common/src/tglobal.c/* * 0: all data are compressed * -1: all data are not compressed * other values: if the message payload size is greater than the tsCompressMsgSize, the message will be compressed. */ int32_t tsCompressMsgSize -1;即默认值-1表示不做消息压缩。从源码注释看该开关同时作用于“客户端向服务端提交写入消息”与“服务端向客户端返回查询结果、metricmeta、多表查询响应”两个方向阈值比较发生在 tglobal.c 的配置注册处取值范围 -1 至 100000000客户端与服务端两侧均生效CFG_SCOPE_BOTH。另外传输层的性能测试工具 cliBench.c 与 svrBench.c 也直接暴露了tsCompressMsgSize命令行参数可用于评估不同压缩阈值下的压测表现。在使用 RESTful 和 WebSocket 连接与 taosAdapter 通信时taosAdapter 支持行业标准的压缩协议允许连接端按标准协议开关传输压缩RESTful 接口客户端在 HTTP 请求头中指定Accept-Encoding告知服务器可接受的压缩类型如 gzip、deflate服务器返回结果时在Content-Encoding头中注明所用压缩算法并返回压缩后的数据WebSocket 接口遵循 WebSocket 压缩协议标准 RFC7692permessage-deflate连接端可按该协议开启/关闭压缩数据备份迁移工具 taosXtaosX 与 taosX Agent 之间的通信也可以开启压缩传输在agent.toml配置文件中设置compressiontrue即可启用。压缩全流程下图展示了 TDengine 引擎在时序数据的整个传输及存储过程中的压缩及解压过程写入路径上数据先按列块执行一级重编码再经二级通用压缩落盘查询路径则反向解压客户端与服务端之间的原生协议消息按compressMsgSize策略做消息级压缩。小结TDengine 的存储压缩是“列式存储 差值/类型定制重编码一级 通用压缩二级”的组合整数与时间戳走差值zigzagsimple8B布尔走位打包/RLE浮点走 delta-delta字符串走字典/LZ4二级则可选 LZ4/ZLIB/ZSTD/XZ 及有损 TSZ各算法在 tcompression.h 与 tcompression.c 中都有清晰的类型化接口编译开启BUILD_WITH_ACCEL_COMPRESS后可通过TAOS_COMPRESS_ACCEL系列环境变量在运行时把硬件加速的 zlib/zstd/lz4 挂入 dispatch table失败自动回退并可用compressBench量化加速收益tcompression_accel.c、compressBench.c传输侧用taos.cfg的compressMsgSize控制原生协议消息压缩阈值默认 -1 关闭REST/WebSocket 走标准 HTTP 压缩头与 RFC7692taosX 通过agent.toml的compressiontrue开启对精度要求宽松的浮点列可评估 TSZ 有损压缩以获得更高压缩率同时用无损模式兜底关键列。按上述源码路径压缩接口、压缩实现、AVX 解码、TSZ 模块、压缩微基准、配置示例可继续深入 TDengine 压缩体系的具体实现细节。【免费下载链接】TDengineHigh-performance, scalable time-series database designed for Industrial IoT (IIoT) scenarios项目地址: https://gitcode.com/GitHub_Trending/tde/TDengine创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考