TDengine SMA 索引 — 块级/文件级统计索引
分类:9.索引 |篇章:02 SMA 索引
适用版本:TDengine v3.x(v3.3.x / v3.4.x) | 最后更新:2026-06-04
SMA(Small Materialized Aggregation)是 TDengine 在数据块/文件级别自动维护的统计索引。它不像传统 B-Tree 索引,而是为聚合查询和过滤剪枝服务。本文聚焦"SMA 作为索引"的视角。
核心概念速查表
| 概念 | 说明 |
|---|---|
| Block SMA | 数据块统计(min/max/sum/count) |
| File SMA | 数据文件统计 |
| Skipping Index | 跳过索引(基于范围) |
| Bloom Filter | 布隆过滤(视版本) |
| Index Effectiveness | 索引有效性 |
详细解析
1. SMA 索引的定位
传统索引(B-Tree)vs SMA 索引: B-Tree 索引(Tag 索引): - 行级精确定位 - 适合等值/范围查询 - 占空间大 SMA 索引: - 块级统计跳过 - 适合聚合 + 大范围过滤 - 占空间极小(< 1%) - 时序数据场景优势明显 定位: - Tag 索引:跨子表筛选 - SMA 索引:单子表内的块剪枝2. Block SMA 工作原理
每个数据块 4096 行,存储元数据: Block Header { time_range: [ts_min, ts_max], row_count: 4096, cols: [ { name: "current", min: 20.5, max: 25.8, sum: 95000, count: 4096 }, { name: "voltage", min: 218, max: 223, sum: 902160, count: 4096 }, { name: "phase", min: 0.45, max: 0.55, sum: 2048, count: 4096 }, ] } 查询时的判断: SELECT * FROM d001 WHERE current > 100 AND ts > T1; 对每个块: if block.time_range.max < T1: 跳过 if block.cols.current.max < 100: 跳过 else: 进入块读取3. SMA 用于聚合优化
SELECT AVG(current) FROM d001 WHERE ts > T1; 执行: ① 找到符合时间范围的所有块 ② 对每个块: - 直接读取 SMA 中的 sum 和 count - 不解码原始数据 ③ 累加:total_sum / total_count 效果: - 100 万行数据:扫描 ~250 个块 - 仅读块 Header(~50KB) - 不读 .data 主体(数 GB) - 毫秒返回 支持函数: ✓ COUNT, SUM, AVG(需 sum/count) ✓ MIN, MAX ✓ FIRST, LAST(部分场景) ✗ PERCENTILE, MEDIAN(需扫行)4. SMA 用于过滤剪枝
WHERE 子句的剪枝: WHERE current > 100 对每个块: if block.cols.current.max <= 100: 整块跳过 if block.cols.current.min > 100: 整块满足,可能加速 else: 块部分匹配,需扫描行 示例: 100 万行分 250 个块 WHERE current > 100: - 200 块 max < 100 → 跳过 - 30 块 min > 100 → 全选 - 20 块部分匹配 → 扫描行 实际扫描行数:20 × 4096 = 82K(节省 92%)5. 时间维度的 SMA
时间列是隐式 SMA 关键: 每块的 time_range 用于时间裁剪: WHERE ts > T → 跳过所有 ts_max < T 的块 WHERE ts BETWEEN T1 AND T2 → 跳过范围外 时间裁剪是最有效的剪枝(时序数据按时间分布) 三级裁剪: ① 文件级:跳过整个 .data 文件 ② 块级:跳过文件内部分块 ③ 行级:块内进一步过滤(按数据列)6. SMA 索引效果监控
-- EXPLAIN ANALYZE 看实际效果EXPLAINANALYZESELECTAVG(current)FROMmetersWHEREts>now-1hANDvoltage>200;-- 关键指标:-- blocks_total: 总块数-- blocks_scanned: 实际扫描的块数-- blocks_skipped: 跳过的块数(SMA 生效)-- rows_examined: 实际读取行数-- rows_returned: 返回行数-- 比率 = blocks_skipped / blocks_total-- 高比率说明 SMA 有效7. 影响 SMA 索引效果的因素
影响因素: ① 块大小(MAXROWS): - 小块:更细粒度剪枝,但块多 - 大块:单块容纳更多数据,剪枝粒度粗 - 默认 4096 通常合适 ② 数据分布: - 同块内数据相似 → 剪枝效果好 - 同块内数据差异大 → 剪枝难 ③ 列类型: - 数值类型剪枝有效 - 字符串类型 SMA 较弱 ④ 数据排序: - 按 ts 自然排序 → 时间剪枝最优 - 数据列乱序 → 数据列剪枝弱8. SMA 索引设计建议
最佳实践: ① 让常查询列与时间有相关性: - 时间相近的数据值也相近 → 块内 min/max 紧凑 - 利于过滤剪枝 ② 控制块大小: - 默认 MAXROWS 4096 适合大多数场景 - 极少行/极多行可能需调整 ③ 不要过度依赖 SMA: - 复杂表达式无法用 SMA - 字符串 LIKE 等无 SMA ④ 配合 Tag 索引: - Tag 索引筛选子表 - SMA 索引剪枝块 - 两者协同最强代码示例
SMA 效果验证
-- 准备数据INSERTINTOd001USINGmeters TAGS('Beijing',1)VALUES(NOW-1d,25,220,0.5);-- ... 写入百万行-- 测试 1: 时间范围(SMA 最有效)EXPLAINANALYZESELECTCOUNT(*)FROMd001WHEREts>now-1h;-- 期望 blocks_skipped 大-- 测试 2: 数据列过滤EXPLAINANALYZESELECTCOUNT(*)FROMd001WHEREcurrent>100;-- 期望部分 blocks_skipped(取决于数据分布)-- 测试 3: 聚合(SMA 直接命中)EXPLAINANALYZESELECTAVG(current)FROMd001WHEREts>now-1d;-- 期望 rows_examined 几乎为 0(直接用 SMA sum/count)提升 SMA 命中
-- ✗ 复杂过滤无法用 SMASELECT*FROMmetersWHEREcurrent>AVG(current);-- ✓ 简单常量比较SELECT*FROMmetersWHEREcurrent>100;-- ✗ 函数包裹SELECT*FROMmetersWHEREABS(phase-0.5)<0.1;-- ✓ 直接范围SELECT*FROMmetersWHEREphaseBETWEEN0.4AND0.6;性能考量
SMA 索引性能特点
| 场景 | SMA 效果 |
|---|---|
| 时间裁剪 | 极高(90%+ 跳过) |
| 数值列过滤 | 中~高(取决于分布) |
| 聚合 SUM/AVG/COUNT | 极高(直接读 SMA) |
| MIN/MAX | 极高(直接读 SMA) |
| 字符串过滤 | 低 |
| 复杂表达式 | 无效 |
存储开销
| 项 | 占比 |
|---|---|
| 块 SMA | < 1% |
| 文件 SMA | < 0.1% |
| TSMA/RSMA | 1%~10% |
| Tag 索引 | < 0.1% |
FAQ
Q1: SMA 索引可以手动创建吗?
块/文件 SMA 自动维护。TSMA/RSMA 需要 CREATE TSMA 或建库 RETENTIONS。
Q2: SMA 索引会失效吗?
写入新数据 → 新块自动有 SMA。Compaction 重算 SMA。极端故障 + 数据修复后 SMA 自动重建。
Q3: 字符串列 SMA 弱怎么办?
- 转为枚举(Tag)
- 用前缀提取列
- 或接受较低性能
Q4: 如何判断 SMA 是否生效?
EXPLAIN ANALYZE 看 blocks_skipped。比率 > 80% 说明有效。
Q5: 不同版本 SMA 行为差异?
总体方向一致。新版本可能增加 Bloom Filter、字符串 min/max 等。具体看版本 Release Note。
参考
系统构架篇
- 01-《TDengine 整体架构全景》
- 02-《集群拓扑深度解析》
- 03-《MNode 内部机制深度解析》
- 04-《RPC 通信层深度解析》
- 05-《VNode 生命周期》
- 06-《RAFT 共识协议》
- 07-《端到端的消息流》
数据模型
- 01-《数据库创建与参数详解》
- 02-《超级表/子表/普通表》
- 03-《支持数据类型深度解析》
- 04-《TDengine Tag 设计哲学与 Schema 变更机制》
- 05-《TDengine 虚拟表实现原理》
存储引擎
- 01-《TDengine 存储引擎概览》
- 02-《TDengine MemTable 深度解析》
- 03-《TDengine WAL 预写日志机制》
- 04-《TDengine 数据文件格式》
- 05-《TDengine Commit 与 Flush 机制 》
- 06-《TDengine Compaction 合并策略 》
- 07-《TDengine 数据保留与 TTL》
- 08-《TDengine 压缩编码机制》
- 09-《TDengine Cache 与 Last 查询加速》
- 10-《TDengine 逻辑计划生成》
查询引擎
- 01-《TDengine 查询引擎概览》
- 02-《TDengine SQL 解析与词法分析》
- 03-《TDengine 语义分析与 AST 重写》
- 04-《TDengine 逻辑计划生成》
- 05-《TDengine 物理计划生成》
- 06-《TDengine 扫描算子》
- 07-《TDengine 聚合算子》
- 08-《TDengine 聚合算子》
- 09-《TDengine 连接算子》
- 10-《TDengine 排序、填充与投影》
- 11-《TDengine 分布式查询执行》
- 12-《TDengine EXPLAIN 与查询优化》
数据写入
- 01-《TDengine SQL INSERT》
- 02-《TDengine 无模式写入》
- 03-《TDengine STMT 写入》
- 04-《TDengine 写入内部流程》
- 05-《TDengine 数据更新删除》
数据订阅
- 01-《TDengine 数据订阅》
- 02-《TDengine 订阅 vs Kafka》
- 03-《TDengine TMQ 消费流程》
- 04-《TDengine 内部机制》
- 05-《TDengine TMQ 最佳实践》
预聚合
- 01-《TDengine RSMA》
- 02-《TDengine TSMA — 时间维度的物化聚合视图》
- 03-《TDengine SMA 内部实现》
索引
- 01-《TDengine Tag 索引》
关于 TDengine
TDengine 专为物联网IoT平台、工业大数据平台设计。其中,TDengine TSDB 是一款高性能、分布式的时序数据库(Time Series Database),同时它还带有内建的缓存、流式计算、数据订阅等系统功能;TDengine IDMP 是一款AI原生工业数据管理平台,它通过树状层次结构建立数据目录,对数据进行标准化、情景化,并通过 AI 提供实时分析、可视化、事件管理与报警等功能。