TDengine Compaction 合并策略 — STT 整理、文件合并与后台调度

分类 :3.存储引擎 | 篇章:06 Compaction

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

Compaction(合并/压实)是后台将多个小文件或无序文件整理为有序大文件的过程。它是写入性能与查询性能之间的平衡器------写入时允许数据以 STT 形式快速落盘,后台再异步整理成查询友好的有序结构。

核心概念速查表

概念 说明
STT Compaction STT 文件数达到阈值时触发的合并
STT_TRIGGER 触发合并的 STT 文件数量阈值
File Merge 将 STT 数据与主数据文件(.data)合并
写放大 一份数据在合并过程中被多次重写
后台线程 Compaction 在专用后台线程执行,不阻塞读写
COMPACT 命令 用户手动触发全库合并

详细解析

1. 为什么需要 Compaction

复制代码
没有 Compaction 的问题:

  高频写入 → MemTable 频繁刷盘 → STT 文件持续累积
  
  查询时:
    FileSet 0: .data(有序) + stt1 + stt2 + stt3 + stt4 + ...
    
    查询需要打开所有文件 + 多路归并排序
    STT 越多 → 查询越慢 → 性能持续退化
    
  Compaction 解决:
    定期将 STT 合并到主文件 → 维持查询性能稳定

2. Compaction 触发条件

触发方式 条件 说明
自动触发 STT 文件数 ≥ STT_TRIGGER Commit 后检查
手动触发 COMPACT DATABASE 用户主动执行
定时触发 COMPACT_INTERVAL(企业版) 按配置的时间间隔自动执行

3. STT Compaction 流程

复制代码
STT Compaction 流程:

  触发:FileSet X 的 STT 文件数 ≥ STT_TRIGGER
       │
       ▼
  ① 选择参与合并的文件:
     - 所有 STT 文件
     - 主文件 .head + .data + .sma(如果需要)
       │
       ▼
  ② 多路归并排序:
     - 为每个 STT 文件和主文件各创建一个读取器
     - 按 (uid, timestamp) 排序键合并
     - 同一 uid 同一时间戳 → 取版本号最高的行
       │
       ▼
  ③ 输出到新文件:
     - 按子表分块(每块 MAXROWS 行)
     - 编码压缩每个数据块
     - 写入新的 .head + .data + .sma
       │
       ▼
  ④ 文件替换:
     - 新文件 fsync
     - 更新 current(原子切换)
     - 删除旧的 STT 文件和旧主文件
       │
       ▼
  ⑤ 完成

4. Compaction 策略

复制代码
STT_TRIGGER 对 Compaction 行为的控制:

  STT_TRIGGER = 1:
    → 每次 Commit 直接与主文件合并
    → 永远只有 .head + .data + .sma,无 STT
    → 写放大最高,但查询最快
    
  STT_TRIGGER = 2(默认):
    → Commit 写入 STT 文件
    → 2 个 STT 时触发合并
    → 均衡策略
    
  STT_TRIGGER = 4~8:
    → 允许 4~8 个 STT 共存
    → 合并频率降低
    → 写放大低,但查询时多路合并开销增加
    
  STT_TRIGGER = 16:
    → 大量 STT 容忍
    → 适合"写多读少"的归档场景
    → 查询时需要合并 16 个文件(较慢)

5. 合并中的数据处理

复制代码
合并过程中的特殊处理:

  ① 版本冲突(同 uid + 同时间戳):
     旧文件: ts=T1, ver=100, value=10.0
     STT:    ts=T1, ver=200, value=10.5
     → 保留 ver=200 的数据(最新版本胜出)
     
  ② 删除标记(.tomb):
     .tomb: uid=1001, delete [T1, T5]
     .data: uid=1001, 有 T1~T10 的数据
     → 合并后:T1~T5 的行被物理删除
     → .tomb 中对应记录也被清理
     
  ③ Schema 版本不同:
     旧数据块 schema_version=1(3列)
     当前 schema_version=3(5列)
     → 合并时按新 Schema 输出
     → 旧数据中不存在的新列填 NULL
     → 已删除的旧列不输出

6. 后台调度机制

复制代码
Compaction 后台线程调度:

  ┌────────────────────────────────────────────┐
  │          Compaction 调度器                   │
  │                                              │
  │  全局队列:                                   │
  │    [VNode2-FileSet3] [VNode5-FileSet1] ...  │
  │                                              │
  │  工作线程数:可配置(默认跟随 CPU 核数)       │
  │                                              │
  │  优先级:                                     │
  │    1. STT 文件最多的 FileSet 优先             │
  │    2. 手动 COMPACT 命令优先                   │
  │                                              │
  │  资源限制:                                   │
  │    - I/O 带宽限制(避免影响前台读写)         │
  │    - 内存限制(合并缓冲区有上限)             │
  └────────────────────────────────────────────┘

7. 手动 COMPACT

sql 复制代码
-- 对整个数据库触发 Compaction
COMPACT DATABASE power;

-- 指定时间范围的 Compaction(企业版)
COMPACT DATABASE power START WITH '2024-01-01' END WITH '2024-06-30';

手动 COMPACT 的用途

  • 大量 DELETE 后回收空间
  • Schema 变更后清理旧格式数据
  • 维护窗口内集中整理,减少日常开销

8. 写放大分析

复制代码
写放大计算:

  假设 STT_TRIGGER = 4:
  
  数据生命周期中被写入磁盘的次数:
    1. MemTable flush → 写入 STT (第 1 次写盘)
    2. 4 个 STT 合并 → 写入主文件(第 2 次写盘)
    
  写放大 = 2(可接受)
  
  如果 STT_TRIGGER = 1:
    1. MemTable flush → 直接合并写入主文件
       但合并时需要读取旧主文件 + 写入新主文件
    写放大 = 2~3(旧数据被重写)
    
  对比:
    STT_TRIGGER 大 → 写放大低(仅合并一次)
    STT_TRIGGER 小 → 写放大高(频繁重写)

代码示例

配置 Compaction 策略

sql 复制代码
-- 写密集型(高吞吐,容忍查询稍慢)
CREATE DATABASE write_heavy STT_TRIGGER 8;

-- 读密集型(查询快,容忍写入开销)
CREATE DATABASE read_heavy STT_TRIGGER 1;

-- 均衡(推荐默认)
CREATE DATABASE balanced STT_TRIGGER 2;

-- 动态调整
ALTER DATABASE power STT_TRIGGER 4;

企业版自动 Compaction

sql 复制代码
-- 每 24 小时自动 Compact 一次
ALTER DATABASE power COMPACT_INTERVAL 24h;

-- 只 Compact 7 天前的数据(避免影响热数据)
ALTER DATABASE power COMPACT_TIME_RANGE -365d, -7d;

-- 在凌晨 2 点开始 Compact
ALTER DATABASE power COMPACT_TIME_OFFSET 2;

性能考量

Compaction 对系统的影响

影响 说明 缓解措施
磁盘 I/O 增加 合并需要读旧文件+写新文件 I/O 限速
CPU 占用 解压旧数据+压缩新数据 后台低优先级
临时磁盘占用 新旧文件短暂共存 预留 20% 磁盘余量
查询延迟抖动 合并期间 I/O 竞争 避开业务高峰

监控 Compaction 状态

bash 复制代码
# 观察 taosd 日志
grep -i "compact\|merge" /var/log/taos/taosdlog.0

# 关注:
# "start to merge, fid:X, sttFiles:N"
# "merge finished, elapsed:XXXms"

FAQ

Q1: Compaction 期间查询会受影响吗?

会有轻微影响。Compaction 占用磁盘 I/O 带宽,可能导致查询的磁盘读取变慢。但不会阻塞查询------查询读旧文件,Compaction 写新文件,逻辑上互不冲突。

Q2: STT_TRIGGER 设多少合适?

  • 子表少(< 1000)+ 每表高频写入 → STT_TRIGGER = 1~2
  • 海量子表(> 10万)+ 每表低频写入 → STT_TRIGGER = 4~8
  • 纯归档(几乎不查询)→ STT_TRIGGER = 8~16

Q3: COMPACT DATABASE 会阻塞写入吗?

不会。COMPACT 在后台执行,前台读写正常进行。但期间磁盘 I/O 压力增大,可能间接影响写入延迟。

Q4: Compaction 可以取消吗?

当前不支持中途取消。一旦开始合并某个 FileSet,会运行到完成。如果需要控制 Compaction 的影响,建议通过 COMPACT_TIME_OFFSET 安排在低峰时段执行。

参考

系统构架篇

数据模型

存储引擎

关于 TDengine

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

相关推荐
anew___1 小时前
《数据库原理》精要解读(八、九、十)—— 事务、恢复与并发:数据库内核的三大支柱
数据库·oracle
J2虾虾1 小时前
Spring AI Alibaba - 多智能体(Multi-agent)
java·人工智能·spring
dblens 数据库管理和开发工具1 小时前
DBLens + 企业微信智能机器人:把数据库 Agent 接入工作群
数据库·机器人·企业微信
方也_arkling1 小时前
【Java-Day13】内部类
java·开发语言
用户298698530141 小时前
Java 开发中读取与解析 Word 文档的实践记录
java·后端
步十人1 小时前
epoll——I/O多路复用技术
linux·数据库·redis
梦幻通灵1 小时前
Java传递负数金额被默认转化为0处理方案
java·开发语言
暴力求解1 小时前
MySQL操作库
数据库·mysql
未若君雅裁1 小时前
ArrayList vs LinkedList:四维对比,该怎么选?
java