news 2026/9/20 12:39:27

TDengine TSZ 有损压缩算法解析:基于 SZ 的浮点误差有界压缩器与 ADT-FSE 编码优化

作者头像

张小明

前端开发工程师

1.2k 24
文章封面图
TDengine TSZ 有损压缩算法解析:基于 SZ 的浮点误差有界压缩器与 ADT-FSE 编码优化
  • 数据库
  • 时序数据库
  • 物联网
  • 大数据
  • 实时分析
  • 云原生

【免费下载链接】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.

项目地址:https://gitcode.com/taosdata/tdengine
点击查看免费下载

导读

TSZ 是 TDengine 内置的误差有界(Error-bounded)有损压缩算法,专为时序数据库中 float/double 类型数据的小块压缩场景而设计。它源自科学计算领域的 SZ 压缩器,经过裁剪、精简与线程安全改造后嵌入 TDengine 存储引擎,用于在允许精度损失的前提下大幅降低浮点数据占用的磁盘空间。本文将结合仓库源码与配置实现,讲解 TSZ 相对 SZ 的核心改进、压缩与解压的调用链、ADT-FSE 编码器的原理与开关方式,以及如何通过fPrecisiondPrecisionifAdtFse等参数在生产环境中启用和调优 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/srczstd的 dictBuilder/common/compress/decompress/deprecated/legacy 等目录会被一起编译为TSZ静态库,并暴露sz/inczstd头文件。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 中的tsCompressTinyinttsCompressInttsCompressString等),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_COMPRESSORZSTD_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_LZ4L2_ZLIBL2_ZSTDL2_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|doublefloat / double / 空
fPrecisionfloat 类型压缩精度,小于此值的尾数部分将被截断浮点数1E-80.0 ~ 100000.0
dPrecisiondouble 类型压缩精度,小于此值的尾数部分将被截取浮点数1E-160.0 ~ 1000000.0
maxRange最大量化区间数(内部参数)整数5000 ~ 65536
curRange当前量化区间数(内部参数)整数1000 ~ 65536
ifAdtFse使用 FSE 替换 Huffman,压缩更快、解压稍慢整数00 / 1
compressor第二级无损压缩器字符串ZSTD_COMPRESSORZSTD_COMPRESSOR/GZIP_COMPRESSOR

其中fPrecisiondPrecision全局配置参数,支持通过 SQL 动态修改并立即生效;ifAdtFsemaxRangecurRangecompressor支持通过 SQL 修改但需重启生效;lossyColumns不支持动态修改。这些参数从 v3.1.0.0 开始引入,除fPrecisiondPrecision外大多在 v3.3.0.0 以后废弃。

5.2 精度参数如何工作

fPrecisiondPrecision是 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 开关的两种状态:

  1. 设置配置lossyColumns = 'float-double'IfAdtFse = 1
  2. 创建 1 张超级表、5 张子表,每张子表插入 50000 行含 null 的 float/double 数据;
  3. 查询验证数据正确性(误差在允许范围内);
  4. 执行alter all dnodes 'IfAdtFse 0'关闭 ADT-FSE;
  5. 重复插入与校验,验证 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.

项目地址:https://gitcode.com/taosdata/tdengine
点击查看免费下载

相关推荐

上一篇:【亲测免费】 多功能同步播放器MultiFunPlayer:开启全新的多媒体互动体验
下一篇:Flow-Go 开源项目安装与使用指南

创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考

版权声明: 本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若内容造成侵权/违法违规/事实不符,请联系邮箱:809451989@qq.com进行投诉反馈,一经查实,立即删除!
网站建设 2026/9/20 12:35:11

ACS880-11变频器快速安装调试要点与Modbus RTU通讯实战指南

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views …

作者头像 李华
网站建设 2026/9/20 12:34:13

OpenClaw 报 HTTP 401 Incorrect API key provided?TaoToken 的 Base URL 这样填

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views …

作者头像 李华
网站建设 2026/9/20 12:33:41

YOLO26零基础环境配置实战:17步跑通边缘部署

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views …

作者头像 李华
网站建设 2026/9/20 12:33:36

PyWebIO初体验:用Python脚本轻松打造网页交互应用

简介:PyWebIO初体验源码包是一套面向Python开发者快速上手PyWebIO库的完整示例资源,适合对Web开发感兴趣但不愿深究前端技术的Python新手与后端工程师。资源配套博文详解了安装、基础用法,并以BMI计算器、Markdown编辑器、聊天室和五子棋游戏…

作者头像 李华
网站建设 2026/9/20 12:32:44

基于数据挖掘与协同过滤的个人服装推荐系统设计与实现

简介:基于数据挖掘的个人服装推荐系统毕业设计资源包,面向计算机科学相关专业毕业论文与课程设计场景。系统采用B/S架构,后端以Django框架与Python语言实现,使用MySQL存储数据,通过Scrapy爬取淘宝女装等服装信息&#…

作者头像 李华