- 数据库
- 时序数据库
- 物联网
- 大数据
- 实时分析
- 云原生
【免费下载链接】tdengine
TDengine is an open source, high-performance, cloud native time-series database optimized for Internet of Things (IoT), Connected Cars, Industrial IoT and DevOps.
导读
TSZ 是 TDengine 内置的误差有界(Error-bounded)有损压缩算法,专为时序数据库中 float/double 类型数据的小块压缩场景而设计。它源自科学计算领域的 SZ 压缩器,经过裁剪、精简与线程安全改造后嵌入 TDengine 存储引擎,用于在允许精度损失的前提下大幅降低浮点数据占用的磁盘空间。本文将结合仓库源码与配置实现,讲解 TSZ 相对 SZ 的核心改进、压缩与解压的调用链、ADT-FSE 编码器的原理与开关方式,以及如何通过fPrecision、dPrecision、ifAdtFse等参数在生产环境中启用和调优 TSZ 有损压缩。
1. TSZ 是什么:面向时序数据库的误差有界浮点压缩器
TSZ(Time Series SZ)是一个"误差有界的有损压缩器(Error-bounded Lossy Data Compressor For Float/Double)",其算法基础来自科学数据压缩领域的 SZ 算法。在 contrib/TSZ/README.md 中,项目明确说明了 TSZ 的定位:它以 SZ 为起点,针对时序数据库场景做了大量精简与优化,最终目标是更快、更小、更独立,尤其适合小数据块的压缩。
在 TDengine 中,TSZ 被封装为静态库参与编译,其构建入口位于 contrib/TSZ/CMakeLists.txt:当BUILD_TSZ_ENABLED打开时,sz/src、zstd的 dictBuilder/common/compress/decompress/deprecated/legacy 等目录会被一起编译为TSZ静态库,并暴露sz/inc与zstd头文件。zstd 在这里扮演的是"第二级无损压缩"的角色,用于对 SZ 有损编码后的中间结果再做一次熵编码压缩。
1.1 与 SZ 的关系
TSZ 算法来源于 SZ 算法。两者的关系可以概括为"同源但面向不同场景":
- SZ 面向科学计算、大规模多维数组数据,提供 1D/2D/3D/4D/5D 等多维接口,支持 int8、int16、int32 等多种数据类型;
- TSZ 面向时序数据库单列数据块,只保留 1D 接口,只支持 float 和 double 两种数据类型,这与时序数据库中绝大多数列都是标量时间序列的事实高度匹配。
从源码看,sz.h 中保留的核心接口只有两条:
SZ_compress_args(int dataType, void *data, size_t r1, unsigned char* outData, sz_params* params):按参数执行压缩,返回输出字节数;SZ_decompress(int dataType, unsigned char *bytes, size_t byteLength, size_t r1, unsigned char* outData):执行解压。
r1即数据点个数(一维长度),这印证了 TSZ 只保留 1D 接口的设计。
2. 相对 SZ 的六大改进
contrib/TSZ/README.md 列出了 TSZ 相对 SZ 的改进清单,结合源码可以逐条展开:
2.1 更快的速度与更小的体积(头部开销从约 24 字节降到 2 字节)
SZ 的压缩结果头部约 24 字节,其中包含大量元信息;TSZ 将其压缩到2 字节。对于时序数据库动辄几十万、上百万个小数据块(block)而言,头部开销的降低直接影响压缩率与存储效率——这是 TSZ "更适合小数据块压缩"的最直接原因。同时 TSZ 删除了一批无用的代码与不必要的函数,进一步减轻了运行时负担。
2.2 支持多线程,接口线程安全
原始 SZ 的接口在多线程并发调用时存在隐患;TSZ 的压缩/解压接口是线程安全的。TDengine 的 vnode 写入与查询天然并发,这一改进保证了 TSZ 可以在多线程写入、多线程查询场景下安全使用。
2.3 移除 2D/3D/4D/5D 函数,仅保留 1D
多维数组是科学计算的常见形态,但时序数据库的数据以单列时间序列为主,多维接口属于冗余。TSZ 移除了所有多维函数,只保留 1D,使代码路径更短、开销更低。
2.4 移除 int8/int16/int32 等数据类型,仅保留 float 与 double
科学数据压缩常需要支持整数类型;而 TDengine 对整数、布尔、字符串等类型已有成熟的无损压缩方案(见 tcompression.h 中的tsCompressTinyint、tsCompressInt、tsCompressString等),TSZ 无需重复支持。因此 TSZ 只针对 float/double 两类浮点数据做有损压缩,与其他类型的无损压缩形成互补。
2.5 优化代码运行速度
通过对热点路径的代码级优化,TSZ 在同等精度约束下拥有更快的压缩和解压速度,这在大量小数据块场景下可以显著降低 CPU 开销。
2.6 其他优化
包括但不限于减少动态内存分配、复用内部缓冲区等细节优化。最终效果正如 README 所述:TSZ 更快、更小、更独立,更适合小数据块压缩。
3. ADT-FSE:面向时序数据库场景的编码优化
3.1 算法定位
ADT-FSE 是 TSZ 中针对时序数据库场景的编码阶段优化算法,其全称对应论文ADT-FSE: A New Encoder for SZ(SC'23,Denver, Colorado,作者 Tao Lu、Yu Zhong、Zibin Sun 等,见 contrib/TSZ/README.md 中的 Citation)。该算法针对小文件(小数据块)场景,能够提供更高的压缩率以及更快的压缩/解压速度。
3.2 与 Huffman 的关系:默认启用,可一键切换
在 SZ/TSZ 的编码流水线中,量化后的符号通常通过 Huffman 编码做熵编码。ADT-FSE 则是用FSE(Finite State Entropy)编码替换 Huffman 编码的优化方案:
- 由 source/common/src/tglobal.c 中的注释可知:
tsIfAdtFse用于在ADT-FSE 算法与原始 Huffman 算法之间切换; - README 说明:默认启用 ADT-FSE,通过
ifAdtFse配置控制; - TDengine 官方配置文档 docs/zh/12-operations-and-tooling/03-components/01-taosd.md 补充说明:FSE 算法压缩速度更快,但解压稍慢,追求压缩速度时可选用此算法。
从源码结构看,FSE 的实现正是来自 zstd 的熵编码模块:contrib/TSZ/zstd/common/fse.h、contrib/TSZ/zstd/compress/fse_compress.c、contrib/TSZ/zstd/decompress/fse_decompress.c。zstd 是公认的高性能熵编码实现,TSZ 直接复用了它的 FSE 编解码器。
3.3 开关与引用
ifAdtFse - 说明:在启用 TSZ 有损压缩时,使用 FSE 算法替换 HUFFMAN 算法,FSE 算法压缩速度更快,但解压稍慢,追求压缩速度可选用此算法。 - 类型:整数,0:关闭,1:打开 - 默认值:0 - 取值范围:0 ~ 1 - 参数类型:全局配置参数 - 动态修改:支持通过 SQL 修改,重启生效注意:README 中说明 ADT-FSE 默认启用,而 source/common/src/tglobal.c 中
tsIfAdtFse的 C 层默认值为false(0)。两者并不矛盾:前者描述的是算法库内部默认行为,后者是 TDengine 进程级配置的默认值。实际生效值以配置文件或ALTER DNODE/ALTER ALL DNODESSQL 设置为准。该参数在 TDengine 中从 v3.1.0.0 引入,v3.3.0.0 以后废弃(相关说明见 docs/zh/12-operations-and-tooling/03-components/01-taosd.md)。
4. TSZ 在 TDengine 中的接入与调用链
4.1 从 SQL 参数到压缩内核的传递链路
TSZ 通过tsCompressInit完成初始化,该函数定义于 source/util/src/tcompression.c,其声明在 include/util/tcompression.h:
void tsCompressInit(char *lossyColumns, float fPrecision, double dPrecision, uint32_t maxIntervals, uint32_t intervals, int32_t ifAdtFse, const char *compressor);实现要点:
lossyFloat = strstr(lossyColumns, "float") != NULL; lossyDouble = strstr(lossyColumns, "double") != NULL; tdszInit(fPrecision, dPrecision, maxIntervals, intervals, ifAdtFse, compressor);即:根据lossyColumns中是否包含float/double子串决定是否对相应列启用有损压缩,然后调用 TSZ 适配层的tdszInit完成底层 SZ 参数配置。
4.2 TSZ 适配层:td_sz.c
TSZ 为 TDengine 提供了专门的适配接口,定义于 contrib/TSZ/sz/src/td_sz.c,头文件为 include/td_sz.h,共四个函数:
| 函数 | 作用 |
|---|---|
tdszInit(float fPrecision, double dPrecision, unsigned int maxIntervals, unsigned int intervals, int ifAdtFse, const char* compressor) | 分配全局参数结构体,设置默认参数后用入参覆盖,并选择GZIP_COMPRESSOR或ZSTD_COMPRESSOR作为第二级无损压缩器 |
tdszCompress(int type, const char *input, const int nelements, const char *output) | 调用SZ_compress_args执行有损压缩,返回输出字节数 |
tdszDecompress(int type, const char *input, int compressedSize, const int nelements, const char *output) | 调用SZ_decompress执行解压,返回输出字节数 |
tdszExit() | 释放全局参数内存 |
tdszInit的核心逻辑(contrib/TSZ/sz/src/td_sz.c):
confparams_cpr->absErrBound = fPrecision; confparams_cpr->absErrBoundDouble = dPrecision; confparams_cpr->max_quant_intervals = maxIntervals; confparams_cpr->quantization_intervals = intervals; confparams_cpr->ifAdtFse = ifAdtFse; if(strcmp(compressor, "GZIP_COMPRESSOR")==0) confparams_cpr->losslessCompressor = GZIP_COMPRESSOR; else if(strcmp(compressor, "ZSTD_COMPRESSOR")==0) confparams_cpr->losslessCompressor = ZSTD_COMPRESSOR;可以看到,fPrecision/dPrecision最终对应 SZ 的绝对误差界(absErrBound),maxIntervals/intervals对应量化区间数,ifAdtFse决定熵编码器选型,compressor决定第二级无损压缩器。
4.3 压缩算法与两级压缩体系
在 include/util/tcompression.h 中,TSZ 被注册为第二级压缩算法L2_TSZ,与其他第二级算法(L2_LZ4、L2_ZLIB、L2_ZSTD、L2_XZ)并列。压缩标志的高 8 位记录第一级算法(如 XOR、RLE、DELTAD 等),低 16 位记录第二级算法与压缩级别。实际压缩时(source/util/src/tcompression.c):
if (l2 == L2_TSZ && lvl != 0 && lossyFloat) { ... } if (l2 == L2_TSZ && lvl != 0 && lossyDouble) { ... }即只有当压缩算法配置为 TSZ 且对应类型的有损开关打开时,才会走tsCompressFloatLossyImp/tsCompressDoubleLossyImp的有损压缩路径。
4.4 单点值压缩的内部流程
从 dataCompression.h 可以看到 TSZ 单值压缩的核心函数:compressSingleFloatValue/compressSingleDoubleValue,它们将一个浮点值分解为"前导相同字节数(lead)+中间精确字节(mid)+残差位(resi)"三元组,compIdenticalLeadingBytesCount_double/compIdenticalLeadingBytesCount_float负责统计相邻值之间相同的头部位数。整列压缩则通过generateLossyCoefficients_float/double生成有损系数,再经compressExactDataArray_float/double完成编码——这正是"先有损量化、再分三段编码、最后熵编码"的 SZ 经典流水线在 TSZ 中的落地。
5. 在 TDengine 中启用 TSZ 有损压缩
TSZ 的启用与调优集中在 taosd 的全局配置参数上。相关参数定义于 source/common/src/tglobal.c,完整说明见官方文档 docs/zh/12-operations-and-tooling/03-components/01-taosd.md。
5.1 参数总览
| 参数 | 说明 | 类型 | 默认值 | 取值范围 |
|---|---|---|---|---|
lossyColumns | 对哪些列启用 TSZ 有损压缩,float|double表示所有 float 和 double 列都可进行有损压缩,置空可关闭 | 字符串 | float|double | float / double / 空 |
fPrecision | float 类型压缩精度,小于此值的尾数部分将被截断 | 浮点数 | 1E-8 | 0.0 ~ 100000.0 |
dPrecision | double 类型压缩精度,小于此值的尾数部分将被截取 | 浮点数 | 1E-16 | 0.0 ~ 1000000.0 |
maxRange | 最大量化区间数(内部参数) | 整数 | 500 | 0 ~ 65536 |
curRange | 当前量化区间数(内部参数) | 整数 | 100 | 0 ~ 65536 |
ifAdtFse | 使用 FSE 替换 Huffman,压缩更快、解压稍慢 | 整数 | 0 | 0 / 1 |
compressor | 第二级无损压缩器 | 字符串 | ZSTD_COMPRESSOR | ZSTD_COMPRESSOR/GZIP_COMPRESSOR |
其中fPrecision、dPrecision是全局配置参数,支持通过 SQL 动态修改并立即生效;ifAdtFse、maxRange、curRange、compressor支持通过 SQL 修改但需重启生效;lossyColumns不支持动态修改。这些参数从 v3.1.0.0 开始引入,除fPrecision、dPrecision外大多在 v3.3.0.0 以后废弃。
5.2 精度参数如何工作
fPrecision与dPrecision是 TSZ 的误差界:压缩时,任何单个浮点值的误差不会超过该界值(这正是"误差有界"的含义)。默认值1E-8(float)与1E-16(double)对绝大多数 IoT 监控场景足够精确:
- 数值越大,允许误差越大,压缩率越高,精度损失越大;
- 数值越小,数据越保真,压缩率越低;
- 需要权衡时,建议从默认值出发,按量级逐步调整。
精度参数在源码中的注册位置见 source/common/src/tglobal.c:cfgAddFloat(pCfg, "fPrecision", tsFPrecision, 0.0f, 100000.0f, ...)与cfgAddFloat(pCfg, "dPrecision", tsDPrecision, 0.0f, 1000000.0f, ...),其上下界与文档描述一致。
5.3 配置示例
在 taos 命令行(taos shell)中执行:
-- 启用 TSZ 有损压缩(默认 lossyColumns 已覆盖 float|double) alter all dnodes 'lossyColumns float|double'; alter all dnodes 'fPrecision 1E-6'; alter all dnodes 'dPrecision 1E-14'; -- 切换熵编码器:开启 ADT-FSE(FSE),追求更快压缩 alter all dnodes 'ifAdtFse 1'; -- 关闭 ADT-FSE,回退到 Huffman alter all dnodes 'ifAdtFse 0';也可以在 packaging/cfg/taos.cfg 中按同样的键值格式写入并重启 taosd 生效。
注意:
fPrecision/dPrecision修改后立即生效;ifAdtFse等参数修改后需重启 taosd。
6. 测试与验证:TSZ 的正确性保障
TDengine 为 TSZ 提供了自动化集成测试,见 test/cases/08-DataCompression/test_compress_tsz.py,其测试流程覆盖了 ADT-FSE 开关的两种状态:
- 设置配置
lossyColumns = 'float-double'、IfAdtFse = 1; - 创建 1 张超级表、5 张子表,每张子表插入 50000 行含 null 的 float/double 数据;
- 查询验证数据正确性(误差在允许范围内);
- 执行
alter all dnodes 'IfAdtFse 0'关闭 ADT-FSE; - 重复插入与校验,验证 Huffman 路径同样正确。
测试中的关键校验逻辑(test/cases/08-DataCompression/test_compress_tsz.py)使用math.isclose分别以rel_tol=1e-06(float)和rel_tol=1e-12(double)验证有损压缩后的查询结果在预期误差范围内,同时验证 null 值的正确保留。
此外,仓库还提供独立的单元测试程序 utils/test/c/tsz_test.c,其中内嵌了大量接近真实时序数据的 float/double 样本数组,直接调用tsCompressFloatLossy/tsCompressDoubleLossy等接口进行压缩、解压并比对,可用于脱离数据库独立验证 TSZ 的压缩/解压正确性。注意该测试只有在编译期定义了TD_TSZ宏时才真正执行,未定义时仅打印提示。
7. 适用场景与选型建议
7.1 适用场景
- IoT / 监控指标存储:温度、湿度、电压、流量等传感器数据天然允许微小误差,是 TSZ 的最佳场景;
- 高压缩率需求:当磁盘成本敏感、数据量大且对精度要求为"有界"而非"无损"时;
- 高写入吞吐:TSZ 相对 SZ 更快的速度,配合 ADT-FSE 更快的压缩编码,可降低写入路径 CPU 开销。
7.2 注意事项
- TSZ 是有损压缩,启用前务必评估业务对精度的容忍度;
fPrecision/dPrecision是硬性误差上界,应据此设定; - 与无损压缩并存:TSZ 只作用于 float/double 列,其余类型仍走 XOR/RLE/LZ4/ZSTD 等原有无损路径;
- ADT-FSE 压缩更快但解压稍慢,读多写少场景可考虑关闭(
ifAdtFse 0),写多读少场景推荐开启; - 小数据块场景下 TSZ 收益最明显,这与它精简头部、只保留 1D 接口的设计初衷一致。
参考文件索引
- TSZ 算法说明:contrib/TSZ/README.md
- TSZ 构建配置:contrib/TSZ/CMakeLists.txt
- TSZ 适配层实现:contrib/TSZ/sz/src/td_sz.c、include/td_sz.h
- SZ 核心接口:contrib/TSZ/sz/inc/sz.h
- SZ 数据压缩内部流程:contrib/TSZ/sz/inc/dataCompression.h
- zstd/FSE 熵编码:contrib/TSZ/zstd/compress/fse_compress.c、contrib/TSZ/zstd/decompress/fse_decompress.c
- TDengine 压缩框架与两级压缩:include/util/tcompression.h、source/util/src/tcompression.c
- 全局配置参数定义:source/common/src/tglobal.c
- 配置参数官方文档:docs/zh/12-operations-and-tooling/03-components/01-taosd.md
- 集成测试:test/cases/08-DataCompression/test_compress_tsz.py
- 独立单元测试:utils/test/c/tsz_test.c
- 数据库
- 时序数据库
- 物联网
- 大数据
- 实时分析
- 云原生
【免费下载链接】tdengine
TDengine is an open source, high-performance, cloud native time-series database optimized for Internet of Things (IoT), Connected Cars, Industrial IoT and DevOps.
相关推荐
TDengine 中的 TSZ 有损压缩算法:面向 Float/Double 的误差有界压缩器与 ADT-FSE 编码优化
TDengine 中的 TSZ 有损压缩算法:面向 Float/Double 的误差有界压缩器与 ADT FSE 编码优化 TSZ 是 TDengine 内置的
数据库时序数据库大数据物联网云原生TDengine 数据压缩机制深度解析:存储压缩、有损压缩与传输压缩的实现原理
TDengine 数据压缩机制深度解析:存储压缩、有损压缩与传输压缩的实现原理 TDengine 作为面向工业物联网(IIoT)的高性能时序数据库,在数据的存储
数据库时序数据库大数据物联网云原生OCRmyPDF 图片优化全解析:从 -O0 到 -O3 的无损压缩与有损压缩调优指南
OCRmyPDF 图片优化全解析:从 O0 到 O3 的无损压缩与有损压缩调优指南 导读 本文以 docs/optimizer.md https://link.
OCRCLI
创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考