TDengine 索引使用指南 — 何时建、怎么建、怎么用

分类 :9.索引 | 篇章 :03 索引使用指南


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

索引设计是数据库性能的根基。本文整合 TDengine 多类索引(Tag 索引、SMA、TSMA、RSMA)的使用决策树,给出"看到查询模式立刻知道该建什么索引"的实用指导。

核心概念速查表

索引 自动/手动 加速对象
块 SMA 自动 块级过滤/聚合
文件 SMA 自动 文件级裁剪
第一 Tag 索引 自动 子表筛选
额外 Tag 索引 手动 多 Tag 筛选
TSMA 手动 长期范围聚合
RSMA 数据库级 分层存储

详细解析

1. 索引决策树

复制代码
查询模式 → 推荐索引:

  ① WHERE ts > T → 块/文件 SMA(自动)
  
  ② WHERE tag = 'v' → Tag 索引
     - 第一 Tag:默认有
     - 其他 Tag:手动 CREATE INDEX
  
  ③ WHERE current > 100 → 块 SMA(自动)
  
  ④ AVG/SUM/MAX/MIN 简单聚合 → 块 SMA + TSMA
  
  ⑤ 大时间范围聚合(月/年)→ TSMA / RSMA
  
  ⑥ 子表筛选高基数 Tag → 重新设计 Schema(Tag 改为列)
  
  ⑦ 复杂表达式聚合 → 流计算

2. 实战 1:电力监控

sql 复制代码
-- 场景:1 亿设备数据,常用查询模式:
-- A. 单设备最近 1 小时数据
-- B. 某城市所有设备聚合
-- C. 全网日报小时聚合

-- 表设计(Tag 决定子表数)
CREATE STABLE meters (
  ts TIMESTAMP, current FLOAT, voltage INT
) TAGS (
  city VARCHAR(32),         -- 200 个城市(低基数)
  district VARCHAR(64),     -- 几万区县
  factory_id VARCHAR(32)    -- 几十万工厂
);
-- 子表数 = 工厂数 × 设备/厂 ≈ 1 亿


-- A 查询: WHERE tbname='d001' AND ts > now-1h
-- → 自动用块 SMA 时间裁剪
-- → 单子表查询,无需 Tag 索引

-- B 查询: WHERE city='Beijing' AND ts > now-1h
-- → 第一 Tag 索引(city)
-- → 自动用块 SMA

-- C 查询: SELECT _wstart, AVG(current) FROM meters 
--          WHERE ts > now-1y INTERVAL(1h)
-- → 创建 TSMA 加速
CREATE TSMA meters_1h ON meters 
FUNCTION(AVG(current), MAX(current), MIN(current)) 
INTERVAL(1h);

3. 实战 2:IoT 网关数据

sql 复制代码
-- 场景:100 万传感器,500 种类型
-- 常用查询:
-- A. 单传感器历史
-- B. 某类型传感器统计
-- C. 异常事件检测

CREATE STABLE sensors (
  ts TIMESTAMP, value FLOAT, status TINYINT
) TAGS (
  sensor_type VARCHAR(32),   -- 500 种(高频过滤)
  factory_id VARCHAR(32),    -- 100 个工厂
  zone_id VARCHAR(32)        -- 几千个区域
);

-- 默认 sensor_type 索引(第一 Tag)
-- factory_id 是常用过滤 → 显式建索引
CREATE INDEX idx_factory ON sensors (factory_id);

-- 异常事件流(实时)
CREATE STREAM s_anomaly INTO anomaly_log AS 
SELECT _wstart, tbname, MAX(value) FROM sensors 
WHERE status > 1 
PARTITION BY tbname 
INTERVAL(1m);

4. 实战 3:金融行情

sql 复制代码
-- 场景:5000 只股票,秒级行情
-- 查询:
-- A. 单股票日 K
-- B. 板块/行业聚合
-- C. 全市场指数

CREATE STABLE quotes (
  ts TIMESTAMP, price FLOAT, volume BIGINT, ...
) TAGS (
  symbol VARCHAR(16),       -- 5000 个股票代码(一对一子表)
  sector VARCHAR(32),       -- 30 个行业
  exchange VARCHAR(16)      -- 几个交易所
);

-- 默认 symbol 索引
-- sector 是常用聚合维度
CREATE INDEX idx_sector ON quotes (sector);

-- 日 K 用 TSMA
CREATE TSMA quotes_1d ON quotes 
FUNCTION(FIRST(price) AS open, MAX(price) AS high, 
         MIN(price) AS low, LAST(price) AS close,
         SUM(volume) AS volume) 
INTERVAL(1d);

-- 历史归档用 RSMA(建库时)
CREATE DATABASE quotes_db 
RETENTIONS '1s:7d,1m:90d,1h:5y';

5. 反模式与改进

复制代码
反模式 1: 高基数 Tag

  CREATE STABLE orders (...) TAGS (user_id BIGINT);
  -- 1 亿用户 = 1 亿子表 = 元数据爆炸
  
  改进:
  CREATE STABLE orders (...) TAGS (region VARCHAR(32), shard INT);
  -- user_id 作为普通列
  -- shard = user_id % 1000 控制子表数


反模式 2: 全字段 Tag

  CREATE STABLE t (...) TAGS (a, b, c, d, e, f, g, h);
  -- 过多 Tag 增加元数据
  
  改进:
  保留 3~5 个常用过滤 Tag,其他作普通列


反模式 3: 长字符串 Tag

  TAGS (description VARCHAR(1024))
  -- Tag 值过大,索引膨胀
  
  改进:
  Tag 仅存类别/ID,详情存普通列


反模式 4: 复杂查询不命中索引

  SELECT * FROM meters WHERE UPPER(location) = 'BEIJING';
  -- 函数包裹 → Tag 索引失效
  
  改进:
  SELECT * FROM meters WHERE location = 'beijing';
  -- 或建表时统一大小写

6. 索引维护操作

sql 复制代码
-- 查看索引
SHOW INDEXES FROM meters;
SHOW TSMAS;

-- 创建索引
CREATE INDEX idx_name ON meters (column);
CREATE TSMA tsma_name ON meters FUNCTION(...) INTERVAL(...);

-- 删除索引
DROP INDEX idx_name;
DROP TSMA tsma_name;

-- 修改不支持,需 DROP 再 CREATE

7. 索引性能验证

复制代码
验证流程:

  ① 准备测试数据
  ② EXPLAIN 看计划
  ③ EXPLAIN ANALYZE 看实际
  ④ 验证关键指标:
     - blocks_skipped 高 → SMA 生效
     - 子表 scan 数量小 → Tag 索引生效
     - rows_examined 小 → 索引覆盖好
  ⑤ 基准测试对比延迟

8. 索引升级策略

复制代码
随业务变化调整:

  ① 监控慢查询日志
  ② 识别高频查询模式
  ③ 评估是否缺索引
  ④ 在业务低峰创建索引(建索引本身耗时)
  ⑤ 验证查询加速效果
  ⑥ 持续观察


何时考虑重构:
  - 子表数失控(亿级以上)
  - Tag 设计不合理
  - 查询模式发生根本变化
  
重构方案:
  - 新建表迁移数据
  - 或重新 ALTER TABLE 修改 Tag(视支持情况)

代码示例

综合应用:智慧城市

sql 复制代码
-- 数据库:分层存储
CREATE DATABASE city_db 
PRECISION 'ms' 
KEEP 1825 
RETENTIONS '1s:30d,1m:1y,1h:5y';
USE city_db;

-- 超级表
CREATE STABLE devices (
  ts TIMESTAMP, value FLOAT, status TINYINT
) TAGS (
  district VARCHAR(32),    -- 区
  device_type VARCHAR(32), -- 类型
  vendor VARCHAR(32)       -- 厂商
) ROLLUP(AVG, MAX, MIN);
-- → 自动 RSMA

-- 显式索引
CREATE INDEX idx_dtype ON devices (device_type);
CREATE INDEX idx_vendor ON devices (vendor);

-- TSMA 加速 5 分钟报表
CREATE TSMA devices_5m ON devices 
FUNCTION(AVG(value), COUNT(*)) 
INTERVAL(5m);

-- 流计算异常检测
CREATE STREAM s_alarm INTO alarm_log AS 
SELECT _wstart, tbname, MAX(value) 
FROM devices WHERE status > 1 
PARTITION BY tbname INTERVAL(10s);

性能考量

不同索引的成本与收益

索引 创建成本 维护成本 查询收益
默认 Tag 索引 0 极低
额外 Tag 索引
块 SMA 0 0
TSMA 中(回填) 极高
RSMA 极高
流计算(替代) 极高

索引设计原则

原则 说明
第一 Tag 最重要 默认有索引,放最常用过滤列
额外索引按需 不要预先加所有索引
TSMA 针对慢查询 仅为已知慢查询模式建
避免高基数 Tag 优先考虑普通列

FAQ

Q1: 索引创建慢怎么办?

大表新建索引耗时长。建议低峰期执行;可考虑表/数据库重建。

Q2: 索引和数据存储分离吗?

Tag 索引在 MNode/VNode 元数据中。SMA 在 .data 文件 Header。TSMA 单独文件。

Q3: 每个查询都需要索引吗?

不需要。简单时间范围查询有自动 SMA 即可。仅在 EXPLAIN 表明扫描过多时才加索引。

Q4: 索引数量上限?

无明确硬上限,但过多索引:

  • 增加写入开销
  • 元数据膨胀
  • 查询优化器选择慢

建议每表 < 5 个手动索引。

Q5: 怎么知道一个查询用了哪个索引?

EXPLAIN VERBOSE 显示选择的索引和扫描方式。

参考

系统构架篇

数据模型

存储引擎

查询引擎

数据写入

数据订阅

预聚合

索引

相关推荐
Industio_触觉智能1 小时前
开源鸿蒙OpenHarmony再进阶,OneConnect规范即将升级为国家级行业标准
嵌入式硬件·物联网·硬件架构·智能硬件·openharmony·开源鸿蒙
ATA88881 小时前
企业数据库账号安全的技术解决方案
数据库·安全
DB哥讲数据库1 小时前
MySQL 8.4 安装教程:超详细图文讲解(附mysql安装包)
linux·数据库·mysql·centos
Databend1 小时前
AST Visitor API 迁移,怎么证明 AI 没改坏 SQL 语义?
数据库·ai编程
TDengine (老段)1 小时前
TDengine DDL 完整参考 — Database/STable/Table/Column/Tag
大数据·数据库·物联网·时序数据库·tdengine·涛思数据
xlxxy_1 小时前
sap获取批次特性报表
java·linux·开发语言·前端·数据库·abap·mm
石像鬼₧魂石1 小时前
【Y2Ksoft】贵阳陈桥饭店ERP管理系统
大数据·前端·物联网·html·数据库架构
栋***t1 小时前
从“工具”到“基建”,麦塔在线考试系统经历的时代变局与定位逻辑
java·大数据·数据库·开源软件·无纸化
SelectDB技术团队1 小时前
Agent 可观测性:Apache Doris / SelectDB 的技术能力、选型对比与实践
数据库·人工智能·agent·可观测·ai-native·apache doris·selectdb