TDengine 查询引擎概览 — SQL 从客户端到结果集的全景流转
·
分类:4.查询引擎 | 篇章:01 查询引擎概览

适用版本:TDengine v3.x(v3.3.x / v3.4.x) | 最后更新:2026-06-07
TDengine 的查询引擎是一套面向时序数据特征定制的分布式 SQL 执行框架。它从客户端解析 SQL 开始,经过语义分析、逻辑/物理计划生成、任务下发、算子并行执行、最终结果汇总,全过程围绕"按时间分片+按子表并行+按列裁剪"的核心理念展开。
核心概念速查表
| 概念 | 说明 |
|---|---|
| Parser | 将 SQL 文本转为抽象语法树(AST) |
| Translator | 语义分析、Catalog 校验、AST 重写 |
| Logical Plan | 与执行引擎无关的关系代数表达 |
| Physical Plan | 落地到具体算子的可执行计划 |
| Scheduler | 任务调度器,决定子查询发到哪个节点 |
| QNode | 专用查询节点(可选,分担 VNode 计算压力) |
| Operator | 执行算子(Scan/Filter/Aggregate/Join/Sort/…) |
| DataSink | 数据汇聚通道(连接算子间或节点间) |
详细解析
1. 查询整体架构
查询执行的端到端流程:
┌─────────────────────────────────────────────────────┐
│ Client (libtaos) │
│ ① Parser → ② Translator → ③ Planner → ④ Scheduler │
└────────────────────────┬────────────────────────────┘
│ RPC 下发任务
┌──────────────────┼──────────────────┐
▼ ▼ ▼
┌──────────┐ ┌──────────┐ ┌──────────┐
│ VNode 1 │ │ VNode 2 │ │ QNode │
│ 执行扫描 │ │ 执行扫描 │ ←─→ │ 汇总聚合 │
│ 本地聚合 │ │ 本地聚合 │ │ 排序合并 │
└──────────┘ └──────────┘ └──────────┘
│ │ │
└──────────────────┴──────────────────┘
│ 结果回传
▼
┌──────────────────┐
│ Client │
│ 收集 + 输出 │
└──────────────────┘
2. 客户端处理阶段
SQL 在客户端的完整生命周期:
SQL 字符串
│
▼
① Tokenizer(词法分析)
拆分为 Token 序列:SELECT, *, FROM, meters, WHERE, ts, >, '...'
│
▼
② Parser(Lemon 生成的 LALR 语法分析器)
构建 AST(抽象语法树):
SelectStmt
├── projection: [*]
├── from: meters
└── where: ts > '...'
│
▼
③ Translator(语义分析)
- 查 Catalog 校验表/列是否存在
- 列名/类型绑定
- Tag 过滤下推到 Catalog 层(仅相关子表参与)
- 重写 AST(如展开 *、补默认窗口等)
│
▼
④ Logical Planner
生成逻辑算子树:
Project
└── Filter (ts > '...')
└── Scan (meters)
│
▼
⑤ Physical Planner
- 分解为多个 SubPlan(按 VGroup 切分)
- 每个 SubPlan 包含物理算子链
- 决定 Exchange 数据流方向
│
▼
⑥ Scheduler
将 SubPlan 下发到对应节点(VNode/QNode)
3. 服务端执行阶段
服务端算子执行模型(Pull-based + Pipeline):
┌──────────────────────────────────────────────────────┐
│ Result Operator(结果输出) │
│ │ getNextBlock() │
│ ▼ │
│ Aggregate Operator │
│ │ getNextBlock() │
│ ▼ │
│ Filter Operator │
│ │ getNextBlock() │
│ ▼ │
│ Scan Operator(读 MemTable + STT + .data) │
└──────────────────────────────────────────────────────┘
特点:
- Pull 模型:上层算子驱动下层算子产出数据
- 列式 DataBlock 在算子间流转(不是行流)
- 一次产出一个 Block(默认 4096 行)
- 算子无状态时可并行(多线程同时跑多个 Scan)
4. 数据传输:Exchange 与 DataSink
节点间的数据流:
VNode 1 Scan VNode 2 Scan VNode 3 Scan
│ │ │
▼ ▼ ▼
DataSink: DataSink: DataSink:
ShuffleDispatch ShuffleDispatch ShuffleDispatch
│ │ │
└──────────┬─────┴──────────────────┘
▼
┌─────────────────┐
│ Exchange Operator│ ← QNode 或 Client 端
│ (按需 Fetch) │
└────────┬────────┘
▼
Aggregate / Sort
5. 时序场景的查询优化点
| 优化 | 说明 |
|---|---|
| 时间裁剪 | WHERE 含时间条件 → 仅扫描相关 File Set |
| Tag 索引 | WHERE Tag 条件 → META 索引返回 uid 列表,跳过无关子表 |
| 块级 SMA | COUNT/MIN/MAX/SUM 直接读预聚合,免解压 |
| 列裁剪 | SELECT 中未引用的列不解压、不读取 |
| 并行扫描 | 多 VGroup 并行,多子表并行 |
| 本地预聚合 | VNode 内先聚合再 Exchange,减少网络传输 |
6. QNode 与 VNode 的协作
QNode 的引入:
传统模式(VNode 既存储又计算):
SELECT COUNT(*) FROM big_table GROUP BY tbname
→ 全部计算压在 VNode,CPU 与写入竞争资源
引入 QNode:
Scan 在 VNode 完成(必须,靠近存储)
复杂计算(大规模 Sort/Hash Aggregate/Join)转到 QNode
→ 写入与查询解耦
→ 可独立扩展查询能力
调度规则(简化):
- 简单聚合 → VNode 本地完成
- 跨 VGroup 合并 → QNode 或 Client
- 显式 USE_QNODE hint → 强制 QNode
7. 查询的并发与隔离
查询的并发模型:
┌────────────────────────────────────────┐
│ taosd 进程 │
│ │
│ 查询线程池(vnodeQueryQueue) │
│ ├── worker 1 → 处理 Query Task │
│ ├── worker 2 → 处理 Query Task │
│ └── worker N → 处理 Query Task │
│ │
│ 并发隔离: │
│ - 查询不阻塞写入(独立线程池) │
│ - 长查询不影响短查询(按 Task 调度) │
│ - 内存配额限制单查询占用 │
└────────────────────────────────────────┘
代码示例
观察查询执行计划
-- 查看查询计划
EXPLAIN SELECT avg(current) FROM meters WHERE ts > now - 1h;
-- 查看详细执行统计
EXPLAIN ANALYZE SELECT avg(current) FROM meters WHERE ts > now - 1h;
-- 查看慢查询
SELECT * FROM performance_schema.perf_queries
ORDER BY exec_usec DESC LIMIT 10;
配置查询资源
-- 查询线程数(taos.cfg)
-- numOfVnodeQueryThreads 8
-- 单查询内存上限
ALTER ALL DNODES 'queryBufferSize 10240'; -- MB
-- 启用 QNode
CREATE QNODE ON DNODE 3;
SHOW QNODES;
性能考量
影响查询性能的关键因素
| 因素 | 影响 | 建议 |
|---|---|---|
| WHERE 时间范围 | 决定扫描 File Set 数 | 总是带时间条件 |
| Tag 过滤选择性 | 决定参与子表数 | 高基数 Tag 优先 |
| SELECT 列数 | 决定解压列数 | 只选需要的列 |
| GROUP BY 基数 | 决定中间结果大小 | 避免在大基数 Tag 上 GROUP |
| 并发查询数 | 决定线程争抢 | 通过 QNode 隔离重查询 |
典型查询延迟参考
| 查询类型 | 数据规模 | 延迟 |
|---|---|---|
| LAST() 缓存命中 | 任意 | < 1ms |
| 单子表点查(带 ts) | 1 天数据 | 1~10ms |
| 单超级表聚合 | 1 万子表 × 1 小时 | 50~500ms |
| 跨 VGroup GROUP BY | 100 万子表 × 1 天 | 1~10s |
| 大范围 JOIN | 视数据量 | 秒~分钟级 |
FAQ
Q1: 为什么 SELECT * 比 SELECT col1 慢很多?
列式存储下每列独立压缩。SELECT * 需要解压所有列并重组为行。如果表有 100 列但只关心 1 列,可减少约 99% 的解压和 I/O。
Q2: WHERE 不带时间条件会怎样?
会扫描所有 File Set(可能数十到数百个文件)。在 TB 级数据上可能持续数分钟。生产环境务必带时间条件。
Q3: QNode 是必须的吗?
不是必须。小集群可不部署 QNode,VNode 既存储又计算。当:
- 查询并发高且影响写入
- 集群有大量复杂分析查询
- 希望独立扩展查询能力
建议部署 QNode。
Q4: 查询是按行还是按列执行?
按列。算子之间传递的是列式 DataBlock(每列一个独立数组)。这与 ClickHouse 等列式 OLAP 引擎类似,能高效利用 SIMD 和缓存。
参考
系统构架篇
- 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 数据修复与迁移》
关于 TDengine
TDengine 专为物联网IoT平台、工业大数据平台设计。其中,TDengine TSDB 是一款高性能、分布式的时序数据库(Time Series Database),同时它还带有内建的缓存、流式计算、数据订阅等系统功能;TDengine IDMP 是一款AI原生工业数据管理平台,它通过树状层次结构建立数据目录,对数据进行标准化、情景化,并通过 AI 提供实时分析、可视化、事件管理与报警等功能。
更多推荐



所有评论(0)