TDengine TSMA (窗口预聚集) 用户手册

TSMA (窗口预聚集) 用户手册

一、什么是 TSMA

TSMA(Time-Range Small Materialized Aggregates,时间窗口预聚集)是 TDengine 提供的一种查询加速机制 。其核心思想是:将常用的聚合计算结果按固定时间窗口提前算好并存储下来,查询时直接读取预计算结果,而不需要扫描海量原始数据。

1.1 类比理解

想象你经营一家连锁商店,每天产生数百万条销售流水记录。老板每周一问:"上周每天的总销售额是多少?"

  • 不使用 TSMA:每次都从数百万条流水中逐条累加,耗时数分钟。
  • 使用 TSMA:系统后台每小时/每天自动算好汇总结果。老板一问,直接读取现成的汇总数字,瞬间返回。

1.2 工作原理

复制代码
原始数据 (海量时序记录)
┌──────────────────────────────────────────┐
│ ts, 设备ID, 温度, 电压, 电流 ... 	       │
│ 数亿行数据                            	   │
└──────────────────────────────────────────┘
        │
        │ TSMA 后台自动计算 (基于流计算引擎)
        ▼
预聚集结果表 (按固定时间窗口汇总)
┌──────────────────────────────────────────┐
│ _wstart    │  avg(温度) │ max(电压) │...  │
│ 2024-01-01 │ 36.5	     │ 220.3     │ ... │
│ 2024-01-02 │ 37.1      │ 221.0     │ ... │
└──────────────────────────────────────────┘
        │
        │ 查询优化器自动路由
        ▼
用户查询: SELECT avg(温度) FROM meters WHERE ts > '2024-01-01'
         → 直接读预聚集结果,性能提升 10~100 倍

关键特性:

  • 对用户透明:无需修改查询 SQL,优化器自动判断是否可以走 TSMA
  • 异步计算:后台流计算引擎自动维护,不影响写入性能
  • 最终一致性:数据更新或删除时自动重算受影响的窗口
  • 分层递归:可基于小窗口 TSMA 创建更大窗口的 TSMA,进一步加速

二、适用场景

✅ 推荐使用的场景

场景 说明 示例
大时间跨度聚合查询 查询天、周、月、年级别汇总 查询过去一年的每月平均温度
监控大盘/报表 定期产出固定格式的统计报表 设备运行日报、能耗周报
固定窗口的 INTERVAL 查询 查询窗口为 TSMA 窗口整数倍 每小时/每天的聚合统计
高并发聚合查询 多用户频繁执行相同类型查询 数据看板、BI 工具对接
按设备/标签分组聚合 GROUP BY tbname / tag 列 每个设备的日均温度

❌ 不适合的场景

场景 原因
按普通列过滤后聚合 WHERE 条件包含非时间列过滤时无法使用 TSMA
按普通列 GROUP BY PARTITION/GROUP BY 包含非标签列时无法使用
非标准窗口查询 SESSION、STATE_WINDOW 等非 INTERVAL 窗口不支持
写入远大于读取 TSMA 会增加后台计算开销,若极少查询则不划算
数据频繁更新 频繁更新会触发大量窗口重算

三、快速上手

3.1 前置条件

sql 复制代码
-- 1. 创建 snode(流计算节点),TSMA 依赖流计算引擎
CREATE SNODE ON DNODE 1;

3.2 准备示例数据

sql 复制代码
-- 创建数据库
CREATE DATABASE power VGROUPS 2;
USE power;

-- 创建超级表:智能电表
CREATE TABLE meters (
    ts        TIMESTAMP,
    voltage   INT,          -- 电压
    current   FLOAT,        -- 电流
    power     FLOAT,        -- 功率
    temp      FLOAT         -- 温度
) TAGS (
    location  VARCHAR(64),  -- 位置
    group_id  INT           -- 分组
);

-- 创建子表并写入数据
CREATE TABLE d1001 USING meters TAGS('北京', 1);
CREATE TABLE d1002 USING meters TAGS('上海', 1);
CREATE TABLE d1003 USING meters TAGS('广州', 2);

-- 插入模拟数据(实际场景中数据量为千万至亿级)
INSERT INTO d1001 VALUES
    ('2024-01-01 00:00:00', 220, 10.5, 2310, 25.1),
    ('2024-01-01 00:05:00', 221, 10.3, 2276, 25.3),
    ('2024-01-01 00:10:00', 219, 10.8, 2365, 25.0),
    ('2024-01-01 01:00:00', 222, 10.1, 2242, 25.5),
    ('2024-01-01 02:00:00', 218, 10.9, 2376, 24.8);

INSERT INTO d1002 VALUES
    ('2024-01-01 00:00:00', 225, 11.0, 2475, 26.0),
    ('2024-01-01 00:05:00', 224, 11.2, 2509, 26.1),
    ('2024-01-01 01:00:00', 223, 10.8, 2408, 25.8);

INSERT INTO d1003 VALUES
    ('2024-01-01 00:00:00', 230, 12.0, 2760, 27.0),
    ('2024-01-01 01:00:00', 228, 11.5, 2622, 26.5);

3.3 创建 TSMA

sql 复制代码
-- 创建 10 分钟窗口的 TSMA
CREATE TSMA tsma_10m ON power.meters
    FUNCTION(avg(voltage), max(voltage), min(voltage), avg(current), sum(power), count(ts), avg(temp))
    INTERVAL(10m);

-- 创建 1 小时窗口的递归 TSMA(基于 10 分钟 TSMA 构建)
CREATE RECURSIVE TSMA tsma_1h ON power.tsma_10m INTERVAL(1h);

⏳ 创建后需等待后台流计算完成历史数据的预聚集(可通过 EXPLAIN 确认是否已启用)。

3.4 自动加速查询

创建 TSMA 后,无需修改任何查询 SQL,优化器自动使用 TSMA:

sql 复制代码
-- 查询 1:全表聚合 → 自动使用最大窗口 tsma_1h
SELECT avg(voltage), max(voltage) FROM power.meters;

-- 查询 2:按设备分组 → 自动使用 tsma_1h
SELECT avg(voltage), tbname FROM power.meters GROUP BY tbname;

-- 查询 3:每小时统计 → 自动使用 tsma_1h
SELECT avg(voltage), _wstart FROM power.meters INTERVAL(1h);

-- 查询 4:每 30 分钟统计 → 自动使用 tsma_10m(30 是 10 的整数倍)
SELECT avg(voltage), _wstart FROM power.meters INTERVAL(30m);

-- 查询 5:带时间范围 → 边界部分查原始数据,中间部分使用 TSMA
SELECT avg(voltage) FROM power.meters
    WHERE ts >= '2024-01-01 00:03:00' AND ts < '2024-01-01 02:00:00';

3.5 验证是否使用了 TSMA

sql 复制代码
-- 使用 EXPLAIN 查看执行计划
EXPLAIN VERBOSE TRUE SELECT avg(voltage) FROM power.meters INTERVAL(1h);

-- 输出中若出现 "Table Scan on tsma_1h_tsma_res_stb_" 字样,
-- 即表示查询已走 TSMA 优化路径

四、深入理解 TSMA 的选择策略

优化器按以下规则选择 TSMA:

4.1 无 INTERVAL 的聚合查询

策略:选择包含所有查询函数的最大窗口 TSMA。

sql 复制代码
-- 假设已创建:tsma_10m(avg,max,min,count) 和 tsma_1h(递归,同函数列表)

SELECT avg(voltage), count(ts) FROM meters;
-- → 使用 tsma_1h(最大窗口,函数列表匹配)

SELECT avg(voltage), sum(power) FROM meters;
-- → 使用 tsma_1h(sum(power) 在函数列表中)

SELECT avg(voltage), spread(voltage) FROM meters;
-- → 不使用任何 TSMA(spread 未在函数列表中定义,
--    即使 spread 可由 min + max 推导,但 TSMA 不做此推导)

4.2 有 INTERVAL 的窗口查询

策略:选择查询 INTERVAL 能整除的最大 TSMA 窗口。

sql 复制代码
-- tsma_10m(INTERVAL=10m), tsma_1h(INTERVAL=1h)

SELECT avg(voltage) FROM meters INTERVAL(30m);
-- → 使用 tsma_10m(30 ÷ 10 = 3,整除 ✓)
-- → tsma_1h 不可用(30 ÷ 60 不整除 ✗)

SELECT avg(voltage) FROM meters INTERVAL(2h);
-- → 使用 tsma_1h(120 ÷ 60 = 2,整除 ✓)

SELECT avg(voltage) FROM meters INTERVAL(7m);
-- → 不使用 TSMA(7 ÷ 10 不整除 ✗)

4.3 带 SLIDING 和 OFFSET 的查询

SLIDING 和 OFFSET 也必须是 TSMA 窗口的整数倍。

sql 复制代码
SELECT avg(voltage) FROM meters INTERVAL(1h) SLIDING(30m);
-- → 使用 tsma_10m(INTERVAL=60m, SLIDING=30m,均为 10 的整数倍)

SELECT avg(voltage) FROM meters INTERVAL(1h, 25m) SLIDING(25m);
-- → 使用 tsma_10m 的 5m 版本(若有)
-- → 因为 OFFSET=25m 和 SLIDING=25m 必须都能被 TSMA 窗口整除

4.4 带时间范围的查询(分段扫描)

当查询时间范围的边界不对齐 TSMA 窗口时,优化器会自动分段:

sql 复制代码
SELECT avg(voltage) FROM meters
    WHERE ts >= '2024-01-01 00:03:00' AND ts < '2024-01-01 02:30:00'
    INTERVAL(30m);

执行计划分为三段:

时间段 数据源 原因
00:03:00 ~ 00:09:59 原始表 起始时间未对齐 10m 窗口边界
00:10:00 ~ 01:59:59 tsma_10m 完整的 TSMA 窗口范围
02:00:00 ~ 02:30:00 原始表 结束部分未满窗口(或可用更小的 TSMA)

五、性能最佳实践

5.1 TSMA 窗口设计原则

复制代码
业务层面                           TSMA 设计
┌─────────────────┐            ┌──────────────────┐
│ 秒级聚合很少用 	  │            │ 不必创建秒级 TSMA  │
│ 分钟级报表常见 	  │  ───────→  │ 创建 1m 或 5m TSMA│
│ 小时/天级别最多	  │            │ 递归创建 1h / 1d  │
│ 月/年级别偶尔用	  │            │ 递归创建 1n / 1y  │
└─────────────────┘            └──────────────────┘

核心建议:

  1. 基础窗口取最小公约数

    分析业务中常用的查询窗口,以它们的最大公约数作为基础 TSMA 窗口。例如常查 10m、30m、1h,则基础窗口用 10m。

  2. 逐层递归创建大窗口

    sql 复制代码
    -- 层级 1:基础窗口(最小粒度)
    CREATE TSMA tsma_base ON db.stb FUNCTION(...) INTERVAL(10m);
    
    -- 层级 2:中等窗口
    CREATE RECURSIVE TSMA tsma_1h ON db.tsma_base INTERVAL(1h);
    
    -- 层级 3:大窗口
    CREATE RECURSIVE TSMA tsma_1d ON db.tsma_1h INTERVAL(1d);

    ⚠️ 递归 TSMA 的 INTERVAL 必须是基础 TSMA 的整数倍,且天只能基于 1h 建立,月只能基于 1d 建立。

  3. 函数列表一次性覆盖

    基础 TSMA 的函数列表应包含所有业务需要的聚合函数,递归 TSMA 自动继承同一函数列表。

    sql 复制代码
    -- ✅ 好的做法:一次包含所有常用函数
    CREATE TSMA tsma_base ON db.meters
        FUNCTION(count(ts), avg(voltage), max(voltage), min(voltage),
                 sum(power), avg(current), first(temp), last(temp))
        INTERVAL(5m);
    
    -- ❌ 不好的做法:拆分为多个小 TSMA(浪费流计算资源)
    CREATE TSMA tsma_a ON db.meters FUNCTION(avg(voltage)) INTERVAL(5m);
    CREATE TSMA tsma_b ON db.meters FUNCTION(max(voltage)) INTERVAL(5m);
  4. 注意 count(*) 的写法

    TSMA 不支持 count(*),应创建 count(ts) 替代,查询时 count(*) 会自动匹配。

5.2 典型业务场景配置方案

场景 A:IoT 设备监控大盘
sql 复制代码
-- 采集频率:每秒一条,1000 台设备
-- 大盘展示:实时(最近 5 分钟)、小时级、天级

CREATE TSMA tsma_5m ON iot.sensors
    FUNCTION(avg(temp), max(temp), min(temp), avg(humidity),
             count(ts), last(ts), sum(power))
    INTERVAL(5m);

CREATE RECURSIVE TSMA tsma_1h ON iot.tsma_5m INTERVAL(1h);
CREATE RECURSIVE TSMA tsma_1d ON iot.tsma_1h INTERVAL(1d);

-- 大盘查询自动加速:
-- "最近 24 小时每小时平均温度" → tsma_1h
-- "过去 30 天每天最高温度"     → tsma_1d
-- "所有设备的总功耗"           → tsma_1d
场景 B:能耗月报
sql 复制代码
-- 需求:每月出一份按区域汇总的能耗报告

CREATE TSMA tsma_1h ON energy.meters
    FUNCTION(sum(kwh), avg(kwh), max(kwh), count(ts))
    INTERVAL(1h);

CREATE RECURSIVE TSMA tsma_1d ON energy.tsma_1h INTERVAL(1d);
CREATE RECURSIVE TSMA tsma_1n ON energy.tsma_1d INTERVAL(1n);

-- 月报查询:
SELECT sum(kwh), avg(kwh), location
    FROM energy.meters
    WHERE ts >= '2024-01-01' AND ts < '2024-02-01'
    PARTITION BY location;
-- → 自动使用 tsma_1n

5.3 配置参数调优

参数 默认值 说明 调优建议
querySmaOptimize True 是否启用 TSMA 优化 保持开启;调试时可临时关闭对比性能
maxTsmaCalcDelay 600s 允许的计算延迟 实时性要求高时减小;报表场景可增大到 86400
tsmaDataDeleteMark 1d 流计算中间结果保留时间 有历史数据更新需求时增大此值
maxTsmaNum 10 集群最大 TSMA 数量 按需调整,范围 [0, 10]
sql 复制代码
-- 临时关闭 TSMA(用于对比查询性能)
ALTER LOCAL 'querySmaOptimize' '0';

-- 重新开启
ALTER LOCAL 'querySmaOptimize' '1';

-- 单条查询跳过 TSMA(使用 hint)
SELECT /*+ skip_tsma() */ avg(voltage) FROM meters;

六、SQL 语法参考

6.1 创建 TSMA

sql 复制代码
-- 基于超级表或普通表创建
CREATE TSMA tsma_name ON [db_name.]table_name
    FUNCTION(func_name(column) [, ...])
    INTERVAL(time_duration);

-- 基于已有 TSMA 创建更大窗口(递归 TSMA)
CREATE RECURSIVE TSMA tsma_name ON [db_name.]base_tsma_name
    INTERVAL(time_duration);

参数约束:

  • INTERVAL 范围:1m ~ 1y(1分钟 ~ 1年)
  • 递归 TSMA 的 INTERVAL 必须是基础 TSMA 的整数倍
  • 天(d)只能基于 1h 建立,月(n)只能基于 1d 建立
  • TSMA 名称最大长度:178 个字符
  • 函数参数只能是单个普通列名,不能是标签列或表达式

6.2 支持的聚合函数

函数 示例 备注
count count(ts) 用 count(ts) 代替 count(*)
avg avg(voltage)
sum sum(power)
min min(temp)
max max(voltage)
first first(temp)
last last(ts)
spread spread(voltage)
stddev stddev(current)

6.3 删除 TSMA

sql 复制代码
DROP TSMA [db_name.]tsma_name;

若有递归 TSMA 依赖于当前 TSMA,必须先删除所有递归 TSMA。

6.4 查看 TSMA

sql 复制代码
-- 查看指定库的 TSMA
SHOW [db_name.]TSMAS;

-- 查看所有 TSMA
SELECT * FROM information_schema.ins_tsma;

七、注意事项与限制

7.1 查询限制

以下情况无法使用 TSMA,查询将回退到扫描原始数据:

限制条件 示例
查询函数不在 TSMA 定义中 TSMA 有 avg(c1),查询 spread(c1)
WHERE 过滤普通列 WHERE voltage > 200
GROUP BY 普通列 GROUP BY voltage
查询窗口不是 TSMA 的整数倍 TSMA=10m,查询 INTERVAL(7m)
使用非 INTERVAL 窗口 SESSION、STATE_WINDOW
TSMA 计算延迟超出阈值 大量历史数据正在计算中
函数参数含表达式 avg(c1 + c2)

7.2 DDL 限制

创建 TSMA 后,原始表的以下操作受限

  • ❌ 不能删除被 TSMA 使用的列(需先删 TSMA)
  • ❌ 不能删除/重命名 tag 列(需先删 TSMA)
  • ❌ 不能修改子表 tag 值(需先删 TSMA)
  • ❌ 不能删除有 TSMA 的表(需先删所有 TSMA)
  • ❌ 不能直接删除 TSMA 关联的 stream(需先删 TSMA)
  • ✅ 可以新增列(但新列不在已有 TSMA 中,需新建 TSMA)

7.3 数据一致性

  • TSMA 计算是异步的 ,不保证实时性,但保证最终一致性
  • 历史数据创建 TSMA 后需等待流计算完成,期间新 TSMA 不被使用
  • 数据更新/删除会自动触发受影响窗口的重算
  • 超出 tsmaDataDeleteMark 时间范围的历史窗口修改不会更新 TSMA 结果

八、完整示例:从创建到验证

sql 复制代码
-- ============ 第一步:准备环境 ============
CREATE SNODE ON DNODE 1;
CREATE DATABASE demo VGROUPS 2;
USE demo;

CREATE TABLE sensors (
    ts TIMESTAMP, temp FLOAT, humidity FLOAT, pressure FLOAT
) TAGS (site VARCHAR(32), floor INT);

CREATE TABLE s1 USING sensors TAGS('A栋', 1);
CREATE TABLE s2 USING sensors TAGS('A栋', 2);
CREATE TABLE s3 USING sensors TAGS('B栋', 1);

-- 写入数据...

-- ============ 第二步:创建 TSMA 体系 ============
-- 基础层:5 分钟窗口
CREATE TSMA tsma_5m ON demo.sensors
    FUNCTION(avg(temp), max(temp), min(temp), avg(humidity), count(ts))
    INTERVAL(5m);

-- 中间层:1 小时窗口(递归)
CREATE RECURSIVE TSMA tsma_1h ON demo.tsma_5m INTERVAL(1h);

-- 顶层:1 天窗口(递归)
CREATE RECURSIVE TSMA tsma_1d ON demo.tsma_1h INTERVAL(1d);

-- ============ 第三步:查询自动加速 ============
-- 全局平均温度 → tsma_1d
SELECT avg(temp) FROM demo.sensors;

-- 每小时最高温度 → tsma_1h
SELECT max(temp), _wstart FROM demo.sensors INTERVAL(1h);

-- 按楼层每天平均湿度 → tsma_1d
SELECT avg(humidity), site, floor FROM demo.sensors
    PARTITION BY site, floor INTERVAL(1d);

-- 指定时间范围查询 → 自动分段:边界走原始数据,中间走 TSMA
SELECT avg(temp) FROM demo.sensors
    WHERE ts >= '2024-06-15 08:13:00' AND ts < '2024-06-16 00:00:00'
    INTERVAL(1h);

-- ============ 第四步:验证 ============
-- 查看执行计划
EXPLAIN VERBOSE TRUE
    SELECT avg(temp) FROM demo.sensors INTERVAL(1h);

-- 对比查询结果一致性
ALTER LOCAL 'querySmaOptimize' '1';
SELECT avg(temp) FROM demo.sensors INTERVAL(1h);

ALTER LOCAL 'querySmaOptimize' '0';
SELECT avg(temp) FROM demo.sensors INTERVAL(1h);
-- 两次结果应完全一致

-- ============ 清理 ============
-- 删除顺序:从顶层递归 TSMA 开始
DROP TSMA demo.tsma_1d;
DROP TSMA demo.tsma_1h;
DROP TSMA demo.tsma_5m;

九、常见问题

Q: 创建 TSMA 后查询没有加速?

A: 检查以下项:

(1) querySmaOptimize 是否为 True

(2) 用 EXPLAIN 确认是否走了 TSMA;

(3) 流计算是否已完成历史数据处理(刚创建时需等待)。

Q: TSMA 和数据库的 Block SMA 有什么区别?

A: Block SMA 是存储引擎自动维护的每个数据块内的最小聚合信息,粒度很小。

TSMA 是用户定义的、按时间窗口的预计算,窗口可以从分钟到年,粒度更大,加速效果更显著。

Q: 能否对子表创建 TSMA?

A: 不能。TSMA 只能基于超级表或普通表创建,不支持子表。

Q: TSMA 占用多少存储空间?

A: TSMA 输出为一张超级表,每行存储聚合函数的中间结果(通常比原始数据大)。但由于行数大幅减少(原始数据行数 ÷ 窗口内行数),总体存储增量有限。

Q: 为什么 spread(c1) 不能使用定义了 min(c1) 和 max(c1) 的 TSMA?

A: TSMA 不做函数间的逻辑推导。即使 spread = max - min,也必须显式在函数列表中包含 spread(c1)

关于 TDengine

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

相关推荐
九.九10 小时前
ops-transformer:AI 处理器上的高性能 Transformer 算子库
人工智能·深度学习·transformer
春日见10 小时前
拉取与合并:如何让个人分支既包含你昨天的修改,也包含 develop 最新更新
大数据·人工智能·深度学习·elasticsearch·搜索引擎
小高不会迪斯科10 小时前
CMU 15445学习心得(二) 内存管理及数据移动--数据库系统如何玩转内存
数据库·oracle
恋猫de小郭10 小时前
AI 在提高你工作效率的同时,也一直在增加你的疲惫和焦虑
前端·人工智能·ai编程
deephub11 小时前
Agent Lightning:微软开源的框架无关 Agent 训练方案,LangChain/AutoGen 都能用
人工智能·microsoft·langchain·大语言模型·agent·强化学习
e***89011 小时前
MySQL 8.0版本JDBC驱动Jar包
数据库·mysql·jar
l1t11 小时前
在wsl的python 3.14.3容器中使用databend包
开发语言·数据库·python·databend
大模型RAG和Agent技术实践11 小时前
从零构建本地AI合同审查系统:架构设计与流式交互实战(完整源代码)
人工智能·交互·智能合同审核
老邋遢11 小时前
第三章-AI知识扫盲看这一篇就够了
人工智能
互联网江湖11 小时前
Seedance2.0炸场:长短视频们“修坝”十年,不如AI放水一天?
人工智能