TDengine 压缩编码机制 — 双层压缩架构与类型特化算法

分类 :3.存储引擎 | 篇章 :08 压缩编码


适用版本:TDengine v3.x(v3.3.x / v3.4.x) | 最后更新:2026-06-02

时序数据具有高度的规律性------时间戳单调递增、数值列缓慢变化、字符串列频繁重复。TDengine 利用这些特征设计了"一阶类型特化编码 + 二阶通用压缩"的双层压缩架构,在不牺牲查询速度的前提下实现极高的压缩比。

核心概念速查表

概念 说明
COMP 数据库压缩级别参数(0/1/2)
一阶编码 类型感知的轻量编码(Delta、XOR等)
二阶压缩 通用字节流压缩(LZ4 / ZSTD / TSZ)
Delta-of-Delta 时间戳编码:存储差值的差值
XOR 差分 浮点数编码:只存储异或后的变化位
Simple8B 整数编码:将多个小值打包到 64 位
ZigZag 有符号整数映射为无符号整数

详细解析

1. 双层压缩架构

复制代码
TDengine 压缩的两个层次:

  原始数据
     │
     ▼
  ┌────────────────────────────────────────────┐
  │ 一阶:类型特化编码                           │
  │                                              │
  │ 根据列的数据类型选择最佳编码算法:             │
  │  - TIMESTAMP → Delta-of-Delta               │
  │  - INT/BIGINT → ZigZag + Simple8B           │
  │  - FLOAT/DOUBLE → XOR 差分                  │
  │  - BOOL → Bit Packing                      │
  │  - BINARY/NCHAR → 字典编码                  │
  │                                              │
  │ 特点:轻量、解码快、利用数据特征             │
  └────────────────────────────────────────────┘
     │
     ▼
  ┌────────────────────────────────────────────┐
  │ 二阶:通用压缩(COMP=2 时启用)              │
  │                                              │
  │ 对一阶编码后的字节流进行通用压缩:            │
  │  - LZ4:高速压缩/解压,压缩率中等            │
  │  - ZSTD:高压缩率,解压稍慢                  │
  │  - TSZ:浮点专用有损压缩(可选)              │
  │                                              │
  │ 特点:进一步压缩残余冗余                     │
  └────────────────────────────────────────────┘
     │
     ▼
  磁盘存储

2. COMP 参数

COMP 值 含义 行为
0 不压缩 原始数据直接写盘(调试用)
1 一阶压缩 仅类型特化编码
2(默认) 双层压缩 一阶编码 + 二阶通用压缩
sql 复制代码
-- 创建数据库时指定压缩级别
CREATE DATABASE power COMP 2;

-- 修改压缩级别(影响后续写入,不影响已有数据)
ALTER DATABASE power COMP 1;

3. 时间戳编码:Delta-of-Delta

复制代码
Delta-of-Delta 编码过程:

  原始时间戳(等间隔采集 1 秒):
    [1000, 1001, 1002, 1003, 1004, 1005]
    
  Step 1: 计算一阶差值(Delta):
    [1000, 1, 1, 1, 1, 1]
    
  Step 2: 计算二阶差值(Delta-of-Delta):
    [1000, 1, 0, 0, 0, 0]
    
  存储:只需存第一个值 + 第一个 Delta + 后续的 0
  
  为什么有效:
  - 时序数据采集间隔通常固定(1s / 5s / 1min)
  - 二阶差值大多为 0 或非常小的数
  - 0 和小值可以用极少位数表示(Simple8B 打包)
  
  非等间隔情况:
    时间戳: [1000, 1001, 1003, 1004, 1006]
    Delta:   [1000, 1, 2, 1, 2]
    D-of-D:  [1000, 1, 1, -1, 1]
    → 虽然不全是 0,但数值仍然很小

4. 整数编码:ZigZag + Simple8B

复制代码
ZigZag 编码(处理有符号整数):

  问题:-1 的补码表示需要 64 位全 1
  ZigZag 映射:有符号 → 无符号
    0  → 0
    -1 → 1
    1  → 2
    -2 → 3
    2  → 4
    ...
  
  效果:小绝对值映射为小无符号数


Simple8B 编码(紧凑打包):

  将多个小值打包到一个 64-bit 整数中:
  
  ┌─────────────────────────────────────────────────────────┐
  │ 64-bit word                                              │
  │                                                          │
  │ [4-bit selector] [60-bit payload]                       │
  │                                                          │
  │ selector 决定 payload 的解读方式:                       │
  │   selector=1: 60 × 1-bit 值(60个布尔值)               │
  │   selector=2: 30 × 2-bit 值(30个0~3的数)              │
  │   selector=3: 20 × 3-bit 值                             │
  │   ...                                                    │
  │   selector=15: 4 × 15-bit 值                            │
  └─────────────────────────────────────────────────────────┘
  
  效果:如果 Delta-of-Delta 都是 0,60 个值只占 8 字节

5. 浮点数编码:XOR 差分

复制代码
XOR 差分编码(Gorilla 算法变体):

  原始浮点数:[25.3, 25.4, 25.3, 25.5, 25.4]
  
  IEEE 754 二进制表示(示意):
    25.3 → 0 10000011 10010100110...
    25.4 → 0 10000011 10010110011...
    
  XOR 编码:
    第一个值: 完整存储
    后续值: current XOR previous
    
    25.3 XOR 25.4 = 0000...00010001...(大部分位为 0)
    
  存储 XOR 结果:
    - 如果 XOR = 0(值未变)→ 存 1 bit: '0'
    - 如果 XOR ≠ 0 → 存前导零数 + 有效位数 + 有效位
    
  为什么有效:
  - 传感器数据通常缓慢变化(温度 25.3 → 25.4)
  - 相邻值的 IEEE 754 表示大部分位相同
  - XOR 后只有少量位不同 → 极少字节即可表示

6. 布尔值编码:Bit Packing

复制代码
BOOL 列编码:

  原始:[true, false, true, true, false, true, true, true]
  
  Bit Packing:
  ┌───┬───┬───┬───┬───┬───┬───┬───┐
  │ 1 │ 0 │ 1 │ 1 │ 0 │ 1 │ 1 │ 1 │  = 1 字节
  └───┴───┴───┴───┴───┴───┴───┴───┘
  
  8 个 BOOL 值 → 1 字节(压缩率 8:1)

7. 字符串编码

复制代码
BINARY/NCHAR 列编码策略:

  策略一:字典编码(低基数)
    原始值:["OK", "ERROR", "OK", "OK", "ERROR", "OK"]
    
    字典:{0: "OK", 1: "ERROR"}
    编码后:[0, 0, 0, 0, 1, 0]  → 每个值仅 1~2 字节
    
    适用:状态码、枚举值、设备型号等重复度高的列

  策略二:前缀压缩 + LZ4(高基数)
    原始值:["sensor_001_temp", "sensor_001_humi", ...]
    → 直接使用 LZ4/ZSTD 压缩(利用公共前缀)
    
    适用:设备 ID、日志文本等

8. NULL 值处理

复制代码
NULL Bitmap 编码:

  每列有一个位图标记 NULL 位置:
  
  ┌───┬───┬───┬───┬───┬───┬───┬───┐
  │ 0 │ 0 │ 1 │ 0 │ 0 │ 1 │ 0 │ 0 │  = NULL 位图
  └───┴───┴───┴───┴───┴───┴───┴───┘
    V   V   N   V   V   N   V   V
    
  V=有效值,N=NULL
  
  NULL 值的列中:
  - Values 数组只存非 NULL 值? 不是------
  - TDengine 为 NULL 位置仍保留占位空间(简化随机访问)
  - 但连续 NULL 区域在二阶压缩时会被高效压缩
  
  全 NULL 优化:
  - 如果整列全是 NULL → flag 标记 HAS_NONE
  - 不存储 Values 数组(0 字节)

9. 压缩率参考

数据类型 场景 典型压缩率(COMP=2)
TIMESTAMP 等间隔采集 20:1 ~ 50:1
INT 缓慢变化(如转速) 5:1 ~ 15:1
FLOAT 传感器数据 3:1 ~ 8:1
BOOL 开关状态 8:1 ~ 16:1
BINARY 低基数枚举 10:1 ~ 30:1
BINARY 高基数文本 2:1 ~ 4:1

综合压缩率 :典型 IoT 场景整体压缩率在 6:1 ~ 20:1 之间。

代码示例

观察压缩效果

sql 复制代码
-- 查看超级表磁盘占用及压缩率
show table distributed power.meters\G;

配置压缩参数

sql 复制代码
-- 高压缩率配置(存储优先)
CREATE DATABASE archive COMP 2;

-- 低延迟配置(性能优先)
CREATE DATABASE realtime COMP 1;

-- 不压缩(调试/对比用)
CREATE DATABASE debug_db COMP 0;

性能考量

压缩与查询延迟的权衡

COMP 写入速度 磁盘占用 查询解码开销
0 最快(无压缩) 最大
1 快(轻量编码) 中等
2 中(双层压缩) 最小

为什么 COMP=2 查询不一定慢?

复制代码
压缩的隐藏收益:

  无压缩(COMP=0)查询一个块:
    磁盘读取 400KB → 耗时 4ms(假设 100MB/s)
    解压:0ms
    总计:4ms
    
  COMP=2 查询一个块:
    磁盘读取 50KB → 耗时 0.5ms
    LZ4 解压 50KB → 耗时 0.1ms
    一阶解码 → 耗时 0.1ms
    总计:0.7ms ← 反而更快!
    
  原因:磁盘 I/O 通常是瓶颈
  压缩减少 I/O 量 > 增加的 CPU 解码时间
  → 对 I/O 受限的查询,压缩反而加速

编码选择对 CPU 的影响

编码算法 编码速度 解码速度 说明
Delta-of-Delta ~2 GB/s ~3 GB/s 极轻量
Simple8B ~1.5 GB/s ~2.5 GB/s 位操作为主
XOR 差分 ~1 GB/s ~2 GB/s 位操作+分支
LZ4 ~400 MB/s ~2 GB/s 解码远快于编码
ZSTD ~100 MB/s ~800 MB/s 高压缩但编解码慢

FAQ

Q1: 能否为不同列指定不同的压缩算法?

TDengine v3.x 中压缩算法由系统根据列类型自动选择,用户不能逐列指定编码方式。COMP 参数是数据库级别的开关。

Q2: COMP=2 写入是否明显变慢?

通常不明显。一阶编码开销极低(位操作),二阶压缩(LZ4)也设计为高速。在 CPU 充足的环境下,压缩减少的磁盘写入量可能反而提升写入吞吐。

Q3: 修改 COMP 后旧数据会重新压缩吗?

不会。COMP 参数只影响后续写入的新数据块。已存储的数据块保持原压缩格式。如需统一压缩格式,可以执行 COMPACT DATABASE 触发重写。

Q4: TSZ 有损压缩是什么?

TSZ 是针对浮点数的有损压缩算法(企业版可选),允许在指定精度范围内丢失最低有效位,换取更高的压缩率。适用于对精度要求不高的监控数据(如温度精确到 0.1°C 即可)。

参考

系统构架篇

数据模型

存储引擎

关于 TDengine

TDengine 专为物联网IoT平台、工业大数据平台设计。其中,TDengine TSDB 是一款高性能、分布式的时序数据库(Time Series Database),同时它还带有内建的缓存、流式计算、数据订阅等系统功能;TDengine IDMP 是一款AI原生工业数据管理平台,它通过树状层次结构建立数据目录,对数据进行标准化、情景化,并通过 AI 提供实时分析、可视化、事件管理与报警等功能。

相关推荐
妄想出头的工业炼药师2 小时前
LVIO鲁棒
算法·开源
苏渡苇2 小时前
Redis 持久化——RDB 快照 vs AOF 日志
数据库·redis·缓存·redis持久化·aof vs rdb
aini_lovee2 小时前
MATLAB 图像修复 — 偏微分方程方法
算法
Cthy_hy2 小时前
Python算法竞赛:排列组合核心用法
开发语言·python·算法
l1t2 小时前
DeepSeek总结的使用 PEG 实现运行时可扩展的 SQL 解析器
数据库·sql
大圣编程3 小时前
面向对象深度理解
java·开发语言·算法
这个DBA有点耶3 小时前
COUNT进阶(续):超大表去重计数的极致优化
数据库·架构·代码规范
宏电物联网3 小时前
从被动救火到主动运维,宏电三三云重新定义 IoT 管理
物联网·iot·物联网平台
爱喝水的鱼丶3 小时前
SAP-ABAP:SAP 简单报表输出开发系列(共6篇) 第四篇:SAP 报表异常处理机制:数据校验与消息提示规范落地
开发语言·数据库·学习·算法·sap·abap