跳到主要内容

执行计划

EXPLAIN 用于查看查询语句的执行计划。它适合在 SQL 调优、慢查询诊断、标签索引命中分析、过滤下推确认、跨节点数据交换排查等场景中使用。

EXPLAIN ANALYZE 会在执行计划的基础上补充运行期指标,可用于判断瓶颈究竟在扫描、过滤、排序、网络交换还是跨 vgroup 的数据倾斜。

语法​

EXPLAIN [ANALYZE] [VERBOSE {true | false}] query_or_subquery;

参数说明​

ANALYZE​

执行语句并返回运行期指标。与普通 EXPLAIN 相比,它会额外展示:

  • 算子首行返回时间、末行返回时间、输出行数
  • 算子执行耗时与等待耗时
  • 扫描 I/O 代价
  • 跨节点数据交换代价
  • 计划时间与执行时间

说明

  • ANALYZE 需要实际执行目标查询语句

诊断价值

  • 用于判断慢 SQL 是“计划不好”还是“执行慢”
  • 用于识别热点 vgroup、慢节点和数据倾斜
  • 用于验证过滤、排序、窗口、聚合等算子的真实代价

VERBOSE​

默认值为 false。设置为 true 后,会显示每个算子的详细属性,例如:

  • Output
  • Filter
  • Primary Filter
  • Tag Index Filter
  • Time Range
  • Time Window
  • Merge ResBlocks
  • Sort Key
  • Sort Method
  • Network
  • Exec cost
  • I/O cost

诊断价值

  • 适合确认优化器是否做了谓词下推、标签索引下推、主键过滤下推
  • 适合定位排序键、合并键、网络交换模式、窗口参数等细节

query_or_subquery​

query_or_subquery 即被分析的 SELECT 查询(可含子查询),例如
SELECT * FROM (SELECT * FROM meters) t WHERE t.c1 > 10。

常见查询形态包括:

  • 简单查询(单表、超级表、虚拟表)
  • 含 WHERE / GROUP BY / ORDER BY / LIMIT
  • 含窗口(如 INTERVAL、SESSION、STATE_WINDOW、EVENT_WINDOW)
  • 含子查询、关联、嵌套等复杂结构

不适用(无可解释的查询计划,或语法不支持):

  • 数据变更:INSERT、UPDATE、DELETE
  • 元数据与实例管理:DDL(含 CREATE / DROP / ALTER 等)、SHOW ...、 USE ...,以及账户、节点、权限等管理类语句

返回结果说明​

EXPLAIN 的返回结果只有一列,列名为 QUERY_PLAN。每一行是计划树中的一个节点或一条详细统计信息。

如果优化器在编译阶段已经确定查询结果恒为空,例如 EXPLAIN SELECT * FROM meters WHERE 0 = 1 这类常量假条件语句,那么语句会被直接裁剪,不会生成可执行计划,也不会进入实际执行阶段。因此,这类语句通常不会产生可用于诊断的有效 EXPLAIN / EXPLAIN ANALYZE 输出。

执行计划采用树状结构展示:

  • 最上层节点是最终结果的产生位置
  • 缩进越深,越接近底层扫描
  • -> 之后是算子名称
  • VERBOSE true 时,算子下方会追加该算子的详细统计信息

示例:

-> Projection (...)
-> Aggregate (...)
-> Table Scan on meters (...)

阅读建议

  • 从最上往下看,先判断结果是如何生成的
  • 从最下往上看,判断底层扫描、过滤、聚合、排序、交换是如何逐步叠加的
  • 如果是分布式查询,重点关注 Data Exchange、Network、I/O cost 和慢 vgroup 信息

常见算子​

下表列出 EXPLAIN 中常见的算子名称及其诊断价值。

算子名称含义诊断价值
Aggregate聚合无分组聚合
Block Dist Scan on ...数据块分布扫描用于块级分布或块级统计场景
Count计数窗口用于 COUNT_WINDOW
Data Exchange N:1多源到单源的数据交换用于观察跨节点拉取成本与网络瓶颈
Dynamic Query Control for ...动态查询控制算子更多用于复杂计划调度信息和内部执行策略判断
Event事件窗口用于 EVENT_WINDOW
External on Column ...外部窗口相关算子用于外部窗口或外部对齐处理
Fill填充算子用于缺失值填充
Group Cache分组缓存算子用于分组缓存和分组复用
Group Sort分组排序用于分组场景下的排序
GroupAggregate分组聚合有分组键的聚合
Indefinite Rows Function返回不定长结果的函数算子用于特殊函数处理
Inner/Left/Right/Full ... Join关联算子重点关注连接算法、连接条件、主键条件和时间范围
Interp插值算子用于 INTERP 插值
Interval on Column ...时间窗口算子适用于 INTERVAL 查询
Last Row Scan on ...Last Row 缓存扫描用于 LAST_ROW、LAST 等查询场景,适合确认是否进行缓存扫描
Merge归并用于多个输入流的合并
Merge Aligned Interval on Column ...对齐窗口归并常见于窗口结果对齐场景
Merge Interval on Column ...窗口归并算子用于分布式窗口聚合合并
Partition on Column ...分片算子常见于数据分片,重点关注分片列信息
Projection投影负责列裁剪、表达式输出、列重排
Session会话窗口用于 SESSION
Sort排序重点关注是否发生显式排序及其排序代价
StateWindow on Column ...状态窗口用于 STATE_WINDOW
System Table Scan on ...系统表扫描用于元数据或系统库查询
Table Count Scan on ...表计数扫描用于表数量相关查询
Table Merge Scan on ...多表合并扫描常见于超级表、多子表聚合或排序场景,适合观察多表归并成本
Table Scan on ...表扫描最常见的底层扫描节点,用于判断扫描顺序、扫描模式、数据加载方式
Tag Scan on ...标签扫描判断是否只基于标签或元数据就能缩小扫描范围
Virtual Table Scan on ...虚拟表扫描判断虚拟表查询是否已经在逻辑层裁剪数据

指标速查​

算子标题行中的常见指标​

这些指标通常出现在 -> 算子名 (...) 这一行中。

指标含义诊断价值
algo=Merge / algo=HashJoin 使用的连接算法用于判断连接瓶颈更偏向排序归并还是哈希构建
asof_op=ASOF JOIN 的比较运算符用于确认最近点连接的比较方向
batch_process_child=是否按批处理子任务用于分析动态计划的执行策略
batch_scan=是否启用批量扫描用于判断超级表或批量子表场景是否走了批量路径
blocking=是否为阻塞型算子,1 表示需要攒够数据后再输出阻塞型算子更容易带来首包延迟
columns=算子参与处理或输出的列数列数过多意味着列裁剪不足,可能增加扫描、网络和内存开销
cost=first..last仅在 EXPLAIN ANALYZE 中出现,表示该算子从创建到首行返回、末行返回的耗时,单位为毫秒用于判断首包慢还是全量处理慢;首值大常见于扫描、网络、排序预热,末值大常见于大结果集或重计算
data_load=data读取原始数据块说明需要真正访问明细数据
data_load=no不需要加载完整数据块说明该查询主要依赖元数据、索引或统计信息
data_load=sma读取 SMA/TSMA 结果说明查询可能命中了预计算路径
functions=当前算子处理的函数数量用于估计聚合、窗口、插值等算子的计算复杂度
global_group=是否为全局分组缓存用于分析分组缓存的范围
group_by_uid=是否按 UID 分组常用于内部分组策略判断
group_join=是否启用分组连接用于复杂连接诊断
groups=分组键数量用于分析分组维度是否过多
has_partition=是否包含分区信息用于判断虚拟表或动态查询是否保留了分区属性
input_order=输入数据按 主键时间戳列 的有序性(asc/desc/unknown)用于判断上游是否已经满足当前算子对时间序的要求
jlimit=单行匹配的最大连接行数用于分析连接放大是否被限制
limit=当前算子承接到的 LIMIT用于确认 LIMIT 是否被尽早下推
mode=grp_order按分组顺序组织扫描说明计划更强调分组输出顺序
mode=seq_grp_order顺序分组扫描常用于按表或按标签分组并保持顺序的场景
mode=sort归并或合并时采用排序模式说明当前合并过程依赖排序键
mode=ts_order按时间序组织扫描常见于普通时间序扫描
offset=当前算子承接到的 OFFSET用于判断偏移是否参与了当前层裁剪
order=[asc|x desc|y]扫描时顺序读取与逆序读取的计数用于判断扫描是否主要按升序还是降序进行
origin_vgroup_num=原始 vgroup 数量用于观察虚拟超级表查询的并行规模
output_order=输出数据按 主键时间戳列 的有序性(asc/desc/unknown)用于判断当前算子是否改变了时间序,进而推断后续是否还能避免排序
partitions=分片键数量用于判断 PARTITION BY 的维度规模
pseudo_columns=伪列数量,例如 _wstart、_wend、tbname 等可用于确认窗口列、表名列等是否被引入执行链路
rows=该算子输出的结果行数用于判断某层是否放大了数据量,或某个 vgroup 是否输出异常偏大
seq_win_grp=是否按顺序窗口分组用于窗口连接和复杂窗口的执行策略判断
slimit=当前算子承接到的 SLIMIT用于分析分片输出数量是否在早期被控制
soffset=当前算子承接到的 SOFFSET用于分析分片偏移是否生效
src_scan=动态控制下的源扫描位置信息主要供复杂计划与支持排障使用
uid_slot=UID 槽位信息主要供复杂计划与支持排障使用
vgroup_slot=vgroup 槽位信息主要供复杂计划与支持排障使用
width=单行宽度,单位为字节宽行会放大扫描、排序、网络交换和内存占用;如果标题行与 Output 行的 width 不同,通常说明算子内部还有中间列或辅助列
window_offset=(x, y)窗口连接偏移范围用于确认时间窗连接的左右边界

说明

  • 在多 vgroup 聚合输出中,a(b) 形式表示“平均值(最大值)”
  • 对 rows= 来说,b 可用于快速定位最重的单个执行节点
  • 对 cost= 来说,首值大多对应首包延迟,末值大多对应总处理时长
  • input_order / output_order 描述的是 主键时间戳列 的有序方向,不是 按 ORDER BY 排序键的方向。因此按非时间戳列排序时(如 ORDER BY val),Sort 算子会显示 output_order=unknown——这表示排序后时间戳顺序已不确定,并不代表结果未排序(结果仍严格按排序键有序)。该字段主要供窗口、归并、Join 等依赖时间序的下游算子判断能否走流式、省去再次排序

VERBOSE true 下的结构与属性指标​

指标含义诊断价值
Buffers:排序缓冲区大小用于判断排序内存压力
End Cond: ...事件窗口结束条件用于确认 EVENT_WINDOW 的结束触发条件
fetch_cost=交换阶段 RPC 拉取耗时网络或远端节点压力的直接体现
fetch_rows=每节点拉取的行数用于判断网络批量大小是否合理
fetch_times=交换阶段的拉取次数次数过多说明数据被切得过碎或下游反复取数
Fill Values: ...填充值列表用于确认 FILL 或 INTERP 的填充值是否正确
Filter: ... efficiency=xx%过滤效率,仅在 ANALYZE 下出现一般越低说明过滤越充分;若过滤条件很强但效率接近 100%,可能没有在预期层级生效
Filter: conditions=...当前算子的过滤条件用于确认过滤谓词处在哪一层执行
Join Col Cond: ...非主键列连接条件用于定位列条件是否下推到连接层
Join Full Cond: ...Join 完整条件用于确认最终连接条件表达式
Join Param: ...Join 的附加参数用于分析 ASOF/WINDOW JOIN 的比较符、偏移范围和限制
Join Prim Cond: ...Join 主键条件用于判断主键连接条件是否被单独抽取
Left Equal Cond:左侧等值列用于分析等值连接键
Left Table Time Range: ...左表时间范围用于时间条件只命中某一侧 Join 的诊断
Left/Right Table Time Range: ...两侧表时间范围用于确认 Join 两边都参与了时间裁剪
loops:排序循环次数循环次数多通常意味着处理批次多或归并轮次多
Merge Key: ...多路归并的键用于确认多分片结果是按什么键归并的
Merge ResBlocks: True/False是否需要归并结果块True 说明当前层存在块级合并,可能带来额外内存与 CPU 开销
Network: mode=...数据交换方式,concurrent 或 sequence并发拉取通常吞吐更高,顺序拉取更容易出现长尾等待
Output: columns=... width=...该算子对下游真正输出的列数与行宽用于确认列裁剪是否生效
Output: Ignore Group Id: true/false该算子输出时是否忽略分组 ID如果为 true,说明后续阶段不再区分上游分组边界
Partition Key: partitions=n分片键信息用于确认 PARTITION BY 是否生效
Primary Filter: ...主键过滤条件,通常是时间戳或主键相关过滤说明主键条件已下推到更底层,通常对性能很重要
Right Equal Cond:右侧等值列用于分析等值连接键
Right Table Time Range: ...右表时间范围用于时间条件只命中某一侧 Join 的诊断
Sort Key: ...排序键用于确认是否按预期列排序
Sort Method: quicksort / merge sort排序算法可用于区分内存内排序和更重的归并排序路径
Start Cond: ...事件窗口开始条件用于确认 EVENT_WINDOW 的起始触发条件
Tag Index Filter: conditions=...可利用标签索引的过滤条件是判断标签索引是否命中的最直接指标
Time Range: [start, end]当前算子的时间扫描范围用于确认时间条件是否被成功裁剪
Time Window: interval=... offset=... sliding=...时间窗口参数用于确认窗口大小、偏移与滑动步长是否符合预期
Window Count=计数窗口大小用于确认 COUNT_WINDOW() 的窗口规模
Window Sliding=计数窗口滑动步长用于确认 COUNT_WINDOW() 的滑动设置
Window: gap=...会话窗口间隔用于诊断 SESSION() 的切分标准

算子的执行代价指标​

EXPLAIN ANALYZE VERBOSE true 会在算子下额外展示 Exec cost 行。

指标含义诊断价值
Exec cost:当前算子的执行代价摘要用于判断当前算子究竟是算得慢、等得久,还是被上游阻塞
compute=算子自身执行时间,不含等待下游数据时间,单位毫秒该值高,通常说明算子本身计算、聚合、排序或扫描开销大
create=算子创建时间;多节点时为平均创建时间与最晚创建时间,时区为系统当前时区多个节点创建时间差异大,可能意味着任务分发或调度不均衡
input_wait=等待下游返回数据的累计时间,单位毫秒高说明瓶颈更可能在子节点或远端扫描
output_wait=等待上游再次调用的累计时间,单位毫秒高说明当前算子产生结果后,上游消费不及时
start=算子从创建到第一次被调用的耗时,单位毫秒值大说明算子虽已创建,但较晚才真正进入执行
times=算子被调用的次数调用次数异常偏多,常意味着上游按很小批次拉取

说明

  • 单节点显示为单值,多节点显示为 平均值(最大值)
  • create= 在多节点时也会显示为 平均时间戳(最晚时间戳)

扫描类算子的 I/O 指标​

扫描类算子在 EXPLAIN ANALYZE VERBOSE true 下还会展示三行 I/O cost 信息。

指标含义诊断价值
check_rows=过滤或检查过的行数若远大于 rows,说明过滤前扫描量很大
composed_blocks=组合生成的块数说明扫描结果经过了额外的块拼装
composed_elapsed=结果块组合耗时值高说明块拼装或重组成本显著
cost_ratio=最慢节点与最快节点的耗时比值比值越大,倾斜越严重
data_deviation=最慢节点相对中位节点的数据量偏差用于辅助判断慢节点是算得慢还是拿到的数据就更多
file_load_blocks=从文件加载的块数值高说明磁盘读取较多
file_load_elapsed=文件块加载耗时判断磁盘 I/O 压力
mem_load_blocks=从内存加载的块数值高通常说明缓存命中较好
mem_load_elapsed=内存块加载耗时内存路径也高时,可能是块数过多或行宽过大
slow_deviation=最慢节点相对中位节点的耗时偏差偏差大说明存在明显长尾
slowest_vgroup_id=最慢 vgroup ID,仅多节点时出现直接定位慢节点
sma_load_blocks=从 SMA/TSMA 加载的块数说明是否命中预聚合数据
sma_load_elapsed=SMA/TSMA 加载耗时用于评估预聚合路径收益
stt_load_blocks=从 STT 相关结构加载的块数用于分析 STT 路径参与程度
stt_load_elapsed=STT 加载耗时判断 STT 路径代价
total_blocks=总处理块数反映扫描总体工作量

诊断建议

  • file_load_* 高:优先检查时间范围、标签过滤和索引命中
  • mem_load_* 高但仍慢:重点检查结果行宽、排序和聚合复杂度
  • sma_load_* 为 0:说明未命中预计算路径
  • cost_ratio、slow_deviation 高:优先定位热点 vgroup、数据分布不均或某个节点资源异常,再通过 data_deviation 辅助定位

计划级汇总指标​

EXPLAIN ANALYZE 在结果末尾还会给出整个语句的汇总信息。

指标含义诊断价值
Execution Time:执行计划实际运行耗时是判断最终端到端耗时的总指标
Planning Time:生成执行计划耗时计划时间高时,应关注 SQL 复杂度、关联层级和优化器开销

子查询​

当查询中包含需要单独生成执行子计划的子查询时,EXPLAIN 的结果通常分为两部分:

  • 先输出主查询(主计划)的计划树
  • 再按独立段落输出子查询对应的子计划,并在段首显示 InitPlan n

这类格式不是把所有子查询逻辑简单缩进到主树下,而是把主计划与子计划分别展示。 InitPlan n 是子查询计划的标题行,用于标识这是一个被主查询 引用 的子计划。 主计划中的算子会通过这个标题表示在何处引用子计划的结果,通常用 $(InitPlan n) 作为占位符。

阅读时可重点关注以下几点:

  • 主计划仍然位于最前面,其中会展示主查询的执行计划,包括主查询的算子、子查询的引用位置(可能由于 verbose 级别被隐藏)等
  • 每个 InitPlan n 后面都会跟着一棵独立的计划树,表示该子查询自身的执行计划
  • 如果存在多个子查询,结果中可能出现多个 InitPlan n
  • Planning Time、Execution Time 仍然位于所有计划输出之后

例如,下面的 SQL 包含两个标量子查询:

SELECT * FROM meters
WHERE
current > (SELECT AVG(current) FROM meters WHERE location = 'Beijing')
AND voltage < (SELECT AVG(voltage) FROM meters WHERE location = 'Beijing');

对应的 EXPLAIN ANALYZE VERBOSE true 结果通常会先给出主计划,再给出子查询的 InitPlan,例如:

-> Projection
-> Table Scan on meters
Filter: conditions=((`demo`.`meters`.`current` > $(InitPlan 1)) AND (`demo`.`meters`.`voltage` < $(InitPlan 2)))
InitPlan 2
-> Aggregate (functions=1)
-> Table Scan on meters
InitPlan 1
-> Aggregate (functions=1)
-> Table Scan on meters

其中:

  • 最前面的 Projection -> Table Scan 是外层主查询
  • InitPlan 1 下面的 Aggregate -> Table Scan 是子查询自己的执行计划
  • 子查询先执行,结果再供主查询中的过滤条件引用。

因此,在分析含子查询语句时,不能只看主计划最上面的几行,也要继续查看后续是否存在 InitPlan n 段,以确认子查询本身是否发生全表扫描、聚合、排序或其他高成本操作。

相关标量子查询​

相关标量子查询显示为其实际求值算子下的 Correlated Scalar Subquery 表达式行,不使用一次性执行的 InitPlan。该行给出模板编号、求值阶段和结果类型;随后列出每个参数的来源作用域及块/槽位,或其继承的参数编号,不显示参数值。Subquery Plan 下展开该相关子查询的执行计划,用于查看内部的扫描、聚合、排序等算子。静态 EXPLAIN 只展示这些计划信息,不执行相关子查询。

EXPLAIN ANALYZE 还在该表达式下显示 Input Rows、Calls 和 Time:分别为实际到达求值位置的行或 JOIN 候选行对、真正发起的内层调用次数、以及这些调用的累计耗时。Calls 不是所属算子的 execTimes;重复参数的独立调用仍分别计数。普通 CASE 中实际执行的分支会计入统计,即使最终未选择该分支的值。父调用耗时包含等待其内层调用的时间,因此不能将不同嵌套层级的 Time 相加当作整条 SQL 的总耗时。

使用示例​

查看普通查询的执行计划​

taos> EXPLAIN SELECT ts, current FROM meters WHERE ts >= '2026-01-01 00:00:00' AND ts < '2026-02-01 00:00:00' \G;
*************************** 1.row ***************************
QUERY_PLAN: -> Data Exchange 4:1 (width=12)
*************************** 2.row ***************************
QUERY_PLAN: -> Projection (columns=2 width=12 input_order=asc)
*************************** 3.row ***************************
QUERY_PLAN: -> Table Scan on meters (columns=2 width=12 order=[asc|1 desc|0] mode=ts_order data_load=data)

查看详细计划​

taos> EXPLAIN VERBOSE true SELECT ts, current FROM meters WHERE ts >= '2025-01-01 00:00:00+08:00' AND ts < '2025-01-02 00:00:00+08:00' \G;
*************************** 1.row ***************************
QUERY_PLAN: -> Data Exchange 4:1 (width=12)
*************************** 2.row ***************************
QUERY_PLAN: Output: columns=2 width=12
*************************** 3.row ***************************
QUERY_PLAN: -> Projection (columns=2 width=12 input_order=asc)
*************************** 4.row ***************************
QUERY_PLAN: Output: columns=2 width=12
*************************** 5.row ***************************
QUERY_PLAN: Output: Ignore Group Id: true
*************************** 6.row ***************************
QUERY_PLAN: Merge ResBlocks: False
*************************** 7.row ***************************
QUERY_PLAN: -> Table Scan on meters (columns=2 width=12 order=[asc|1 desc|0] mode=ts_order data_load=data)
*************************** 8.row ***************************
QUERY_PLAN: Output: columns=2 width=12
*************************** 9.row ***************************
QUERY_PLAN: Time Range: [1735660800000, 1735747199999]

查看执行过程信息​

taos> EXPLAIN ANALYZE VERBOSE true SELECT * FROM meters WHERE location = 'Beijing' \G;
*************************** 1.row ***************************
QUERY_PLAN: -> Data Exchange 4:1 (cost=2.358..9.769 rows=80000 width=39)
*************************** 2.row ***************************
QUERY_PLAN: Output: columns=6 width=39
*************************** 3.row ***************************
QUERY_PLAN: Network: mode=concurrent fetch_times=2.8(5) fetch_rows=20000.0(40000) fetch_cost=5.389(9.244)
*************************** 4.row ***************************
QUERY_PLAN: Exec cost: compute=0.649 create=2026-03-13 16:57:39.481277 start=0.010 times=25 input_wait=7.160 output_wait=1.951
*************************** 5.row ***************************
QUERY_PLAN: -> Projection (cost=2.195(3.248)..7.120(12.106) rows=20000.0(40000) columns=6 width=39 input_order=asc)
*************************** 6.row ***************************
QUERY_PLAN: Output: columns=6 width=39
*************************** 7.row ***************************
QUERY_PLAN: Output: Ignore Group Id: true
*************************** 8.row ***************************
QUERY_PLAN: Merge ResBlocks: False
*************************** 9.row ***************************
QUERY_PLAN: Exec cost: compute=0.095(0.154) create=2026-03-13 16:57:39.475838(2026-03-13 16:57:39.475887) start=0.021(0.023) times=7.0(13) input_wait=5.271(9.039) output_wait=1.759(2.926)
*************************** 10.row ***************************
QUERY_PLAN: -> Table Scan on meters (cost=2.132(3.149)..7.125(12.116) rows=20000.0(40000) columns=4 pseudo_columns=2 width=39 order=[asc|1 desc|0] mode=ts_order data_load=data)
*************************** 11.row ***************************
QUERY_PLAN: Output: columns=6 width=39
*************************** 12.row ***************************
QUERY_PLAN: Time Range: [-9223372036854775808, 9223372036854775807]
*************************** 13.row ***************************
QUERY_PLAN: Tag Index Filter: conditions=(`test`.`meters`.`location` = 'Beijing')
*************************** 14.row ***************************
QUERY_PLAN: Exec cost: compute=5.268(9.033) create=2026-03-13 16:57:39.475829(2026-03-13 16:57:39.475880) start=0.030(0.037) times=7.0(13) input_wait=0.000(0.000) output_wait=1.855(3.053)
*************************** 15.row ***************************
QUERY_PLAN: -> I/O cost: total_blocks=6.0(12) file_load_blocks=0.8(1) stt_load_blocks=0.0(0) mem_load_blocks=0.0(0) sma_load_blocks=0.0(0) composed_blocks=6.0(12)
*************************** 16.row ***************************
QUERY_PLAN: file_load_elapsed=0.000(0.000) stt_load_elapsed=0.000(0.000) mem_load_elapsed=0.000(0.000) sma_load_elapsed=0.000(0.000) composed_elapsed=3.578(6.655)
*************************** 17.row ***************************
QUERY_PLAN: check_rows=20000.0(40000) slowest_vgroup_id=4 slow_deviation=130% cost_ratio=173.7 data_deviation=300%
*************************** 18.row ***************************
QUERY_PLAN: Planning Time: 0.606 ms
*************************** 19.row ***************************
QUERY_PLAN: Execution Time: 24.992 ms

-- 注意:需要手动创建标签索引

子查询​

taos> EXPLAIN ANALYZE VERBOSE true SELECT * FROM meters WHERE voltage > (SELECT AVG(voltage) FROM meters WHERE location = 'Beijing') \G;
*************************** 1.row ***************************
QUERY_PLAN: -> Data Exchange 4:1 (cost=3.021..24.317 rows=149340 width=39)
*************************** 2.row ***************************
QUERY_PLAN: Output: columns=6 width=39
*************************** 3.row ***************************
QUERY_PLAN: Network: mode=concurrent fetch_times=4.8(8) fetch_rows=37335.0(49780) fetch_cost=7.728(9.353)
*************************** 4.row ***************************
QUERY_PLAN: Exec cost: compute=1.067 create=2026-03-23 14:34:10.244872 start=0.010 times=91 input_wait=5.704 output_wait=18.525
*************************** 5.row ***************************
QUERY_PLAN: -> Projection (cost=1.282(1.618)..14.188(17.264) rows=37335.0(49780) columns=6 width=39 input_order=asc)
*************************** 6.row ***************************
QUERY_PLAN: Output: columns=6 width=39
*************************** 7.row ***************************
QUERY_PLAN: Output: Ignore Group Id: true
*************************** 8.row ***************************
QUERY_PLAN: Merge ResBlocks: False
*************************** 9.row ***************************
QUERY_PLAN: Exec cost: compute=0.077(0.107) create=2026-03-23 14:34:10.239770(2026-03-23 14:34:10.239812) start=0.018(0.038) times=23.5(31) input_wait=12.247(15.116) output_wait=1.874(2.509)
*************************** 10.row ***************************
QUERY_PLAN: -> Table Scan on meters (cost=1.269(1.607)..14.195(17.268) rows=37335.0(49780) columns=4 pseudo_columns=2 width=39 order=[asc|1 desc|0] mode=ts_order data_load=data)
*************************** 11.row ***************************
QUERY_PLAN: Output: columns=6 width=39
*************************** 12.row ***************************
QUERY_PLAN: Time Range: [-9223372036854775808, 9223372036854775807]
*************************** 13.row ***************************
QUERY_PLAN: Filter: conditions=(`test`.`meters`.`voltage` > $(InitPlan 1)) efficiency=49.8%
*************************** 14.row ***************************
QUERY_PLAN: Exec cost: compute=12.241(15.108) create=2026-03-23 14:34:10.239762(2026-03-23 14:34:10.239806) start=0.026(0.047) times=23.5(31) input_wait=0.000(0.000) output_wait=1.956(2.597)
*************************** 15.row ***************************
QUERY_PLAN: I/O cost: total_blocks=22.5(30) file_load_blocks=0.0(0) stt_load_blocks=0.0(0) mem_load_blocks=22.5(30) sma_load_blocks=0.0(0) composed_blocks=22.5(30)
*************************** 16.row ***************************
QUERY_PLAN: file_load_elapsed=0.000(0.000) stt_load_elapsed=0.000(0.000) mem_load_elapsed=7.833(9.995) sma_load_elapsed=0.000(0.000) composed_elapsed=7.833(9.995)
*************************** 17.row ***************************
QUERY_PLAN: check_rows=75000.0(100000) slowest_vgroup_id=4 slow_deviation=12.5% cost_ratio=2.6 data_deviation=-11.1%
*************************** 18.row ***************************
QUERY_PLAN: InitPlan 1
*************************** 19.row ***************************
QUERY_PLAN: -> Aggregate (cost=0.418..0.418 rows=1 functions=1 width=8 input_order=asc)
*************************** 20.row ***************************
QUERY_PLAN: Output: columns=1 width=8 blocking=1
*************************** 21.row ***************************
QUERY_PLAN: Merge ResBlocks: True
*************************** 22.row ***************************
QUERY_PLAN: Exec cost: compute=0.017 create=2026-03-23 14:34:10.238829 start=0.011 times=2 input_wait=0.390 output_wait=0.002
*************************** 23.row ***************************
QUERY_PLAN: -> Data Exchange 4:1 (cost=0.354..0.425 rows=3 width=58)
*************************** 24.row ***************************
QUERY_PLAN: Output: columns=1 width=58
*************************** 25.row ***************************
QUERY_PLAN: Network: mode=concurrent fetch_times=1.0(1) fetch_rows=0.8(1) fetch_cost=0.299(0.344)
*************************** 26.row ***************************
QUERY_PLAN: Exec cost: compute=0.056 create=2026-03-23 14:34:10.238815 start=0.025 times=4 input_wait=0.333 output_wait=0.012
*************************** 27.row ***************************
QUERY_PLAN: -> Aggregate (cost=1.206(2.221)..1.206(2.221) rows=0.8(1) functions=1 width=58 input_order=asc)
*************************** 28.row ***************************
QUERY_PLAN: Output: columns=1 width=58 blocking=1
*************************** 29.row ***************************
QUERY_PLAN: Merge ResBlocks: False
*************************** 30.row ***************************
QUERY_PLAN: Exec cost: compute=0.054(0.094) create=2026-03-23 14:34:10.236093(2026-03-23 14:34:10.236152) start=0.016(0.033) times=1.8(2) input_wait=1.145(2.114) output_wait=0.001(0.002)
*************************** 31.row ***************************
QUERY_PLAN: -> Table Scan on meters (cost=0.329(0.561)..1.207(2.203) rows=15000.0(20000) columns=2 pseudo_columns=1 width=30 order=[asc|1 desc|0] mode=ts_order data_load=sma)
*************************** 32.row ***************************
QUERY_PLAN: Output: columns=3 width=30
*************************** 33.row ***************************
QUERY_PLAN: Time Range: [-9223372036854775808, 9223372036854775807]
*************************** 34.row ***************************
QUERY_PLAN: Exec cost: compute=1.143(2.110) create=2026-03-23 14:34:10.236076(2026-03-23 14:34:10.236149) start=0.033(0.058) times=5.5(7) input_wait=0.000(0.000) output_wait=0.042(0.078)
*************************** 35.row ***************************
QUERY_PLAN: I/O cost: total_blocks=4.5(6) file_load_blocks=0.0(0) stt_load_blocks=0.0(0) mem_load_blocks=4.5(6) sma_load_blocks=4.5(6) composed_blocks=4.5(6)
*************************** 36.row ***************************
QUERY_PLAN: file_load_elapsed=0.000(0.000) stt_load_elapsed=0.000(0.000) mem_load_elapsed=1.047(1.958) sma_load_elapsed=0.000(0.000) composed_elapsed=1.047(1.958)
*************************** 37.row ***************************
QUERY_PLAN: check_rows=15000.0(20000) slowest_vgroup_id=2 slow_deviation=80.5% cost_ratio=131.9 data_deviation=0.0%
*************************** 38.row ***************************
QUERY_PLAN: Planning Time: 0.815 ms
*************************** 39.row ***************************
QUERY_PLAN: Execution Time: 64.835 ms

聚合查询​

taos> EXPLAIN ANALYZE VERBOSE true SELECT tbname, COUNT(*), AVG(current) FROM meters PARTITION BY tbname \G;
*************************** 1.row ***************************
QUERY_PLAN: -> Data Exchange 4:1 (cost=0.357..0.563 rows=30 width=288)
*************************** 2.row ***************************
QUERY_PLAN: Output: columns=3 width=288
*************************** 3.row ***************************
QUERY_PLAN: Network: mode=concurrent fetch_times=1.0(1) fetch_rows=7.5(10) fetch_cost=0.408(0.512)
*************************** 4.row ***************************
QUERY_PLAN: Exec cost: compute=0.059 create=2026-03-13 16:29:52.978341 start=0.008 times=5 input_wait=0.491 output_wait=0.007
*************************** 5.row ***************************
QUERY_PLAN: -> Aggregate (cost=5.506(7.403)..5.506(7.403) rows=7.5(10) functions=3 width=288 input_order=unknown )
*************************** 6.row ***************************
QUERY_PLAN: Output: columns=3 width=288 blocking=1
*************************** 7.row ***************************
QUERY_PLAN: Merge ResBlocks: True
*************************** 8.row ***************************
QUERY_PLAN: Exec cost: compute=0.271(0.434) create=2026-03-13 16:29:52.970343(2026-03-13 16:29:52.970453) start=0.802(0.860) times=1.0(1) input_wait=4.433(6.305) output_wait=0.002(0.004)
*************************** 9.row ***************************
QUERY_PLAN: -> Table Scan on meters (cost=1.190(1.321)..5.414(7.396) rows=75000.0(100000) columns=2 pseudo_columns=1 width=284 order=[asc|1 desc|0] mode=ts_order data_load=sma)
*************************** 10.row ***************************
QUERY_PLAN: Output: columns=3 width=284
*************************** 11.row ***************************
QUERY_PLAN: Time Range: [-9223372036854775808, 9223372036854775807]
*************************** 12.row ***************************
QUERY_PLAN: Partition Key: partitions=1
*************************** 13.row ***************************
QUERY_PLAN: Exec cost: compute=4.427(6.299) create=2026-03-13 16:29:52.970329(2026-03-13 16:29:52.970427) start=0.816(0.876) times=23.5(31) input_wait=0.000(0.000) output_wait=0.178(0.246)
*************************** 14.row ***************************
QUERY_PLAN: -> I/O cost: total_blocks=22.5(30) file_load_blocks=0.0(0) stt_load_blocks=0.0(0) mem_load_blocks=22.5(30) sma_load_blocks=22.5(30) composed_blocks=22.5(30)
*************************** 15.row ***************************
QUERY_PLAN: file_load_elapsed=0.000(0.000) stt_load_elapsed=0.000(0.000) mem_load_elapsed=4.229(6.031) sma_load_elapsed=0.000(0.000) composed_elapsed=4.229(6.031)
*************************** 16.row ***************************
QUERY_PLAN: check_rows=75000.0(100000) slowest_vgroup_id=4 slow_deviation=41% cost_ratio=3.8 data_deviation=25%
*************************** 17.row ***************************
QUERY_PLAN: Planning Time: 8.821 ms
*************************** 18.row ***************************
QUERY_PLAN: Execution Time: 10.767 ms

时间窗口查询​

taos> EXPLAIN ANALYZE VERBOSE true SELECT _wstart, _wend, COUNT(*), AVG(current) FROM meters INTERVAL(10s) \G;
*************************** 1.row ***************************
QUERY_PLAN: -> Merge Aligned Interval on Column (cost=0.626..0.626 rows=10 functions=4 width=32 input_order=asc output_order=asc)
*************************** 2.row ***************************
QUERY_PLAN: Output: columns=4 width=32
*************************** 3.row ***************************
QUERY_PLAN: Time Window: interval=10s offset=0a sliding=10s
*************************** 4.row ***************************
QUERY_PLAN: Merge ResBlocks: True
*************************** 5.row ***************************
QUERY_PLAN: Exec cost: compute=0.028 create=2026-03-13 16:34:44.806501 start=0.018 times=1 input_wait=0.580 output_wait=0.002
*************************** 6.row ***************************
QUERY_PLAN: -> Merge (cost=0.605..0.605 rows=40 columns=4 width=82 input_order=asc output_order=asc mode=sort)
*************************** 7.row ***************************
QUERY_PLAN: Sort Method: merge sort Buffers:20.00 Kb loops:2
*************************** 8.row ***************************
QUERY_PLAN: Output: columns=4 width=82
*************************** 9.row ***************************
QUERY_PLAN: Output: Ignore Group Id: false
*************************** 10.row ***************************
QUERY_PLAN: Merge Key: _group_id asc, ts asc
*************************** 11.row ***************************
QUERY_PLAN: Exec cost: compute=0.580 create=2026-03-13 16:34:44.806493 start=0.026 times=2 input_wait=0.000 output_wait=0.028
*************************** 12.row ***************************
QUERY_PLAN: -> Data Exchange 1:1 (cost=0.312..0.312 rows=10 width=82)
*************************** 13.row ***************************
QUERY_PLAN: Output: columns=4 width=82
*************************** 14.row ***************************
QUERY_PLAN: Network: mode=concurrent fetch_times=1 fetch_rows=10 fetch_cost=0.248
*************************** 15.row ***************************
QUERY_PLAN: Exec cost: compute=0.007 create=2026-03-13 16:34:44.806480 start=0.073 times=2 input_wait=0.232 output_wait=0.305
*************************** 16.row ***************************
QUERY_PLAN: -> Interval on Column ts (cost=5.483..5.483 rows=10 functions=4 width=82 input_order=asc output_order=asc)
*************************** 17.row ***************************
QUERY_PLAN: Output: columns=4 width=82
*************************** 18.row ***************************
QUERY_PLAN: Time Range: [-9223372036854775808, 9223372036854775807]
*************************** 19.row ***************************
QUERY_PLAN: Time Window: interval=10s offset=0a sliding=10s
*************************** 20.row ***************************
QUERY_PLAN: Merge ResBlocks: False
*************************** 21.row ***************************
QUERY_PLAN: Exec cost: compute=0.250 create=2026-03-13 16:34:44.800429 start=0.024 times=1 input_wait=5.209 output_wait=0.003
*************************** 22.row ***************************
QUERY_PLAN: -> Table Scan on meters (cost=0.388..5.478 rows=90000 columns=2 width=12 order=[asc|1 desc|0] mode=ts_order data_load=sma)
*************************** 23.row ***************************
QUERY_PLAN: Output: columns=2 width=12
*************************** 24.row ***************************
QUERY_PLAN: Time Range: [-9223372036854775808, 9223372036854775807]
*************************** 25.row ***************************
QUERY_PLAN: Exec cost: compute=5.204 create=2026-03-13 16:34:44.800423 start=0.031 times=28 input_wait=0.000 output_wait=0.247
*************************** 26.row ***************************
QUERY_PLAN: -> I/O cost: total_blocks=27 file_load_blocks=0 stt_load_blocks=0 mem_load_blocks=27 sma_load_blocks=0 composed_blocks=27
*************************** 27.row ***************************
QUERY_PLAN: file_load_elapsed=0.000 stt_load_elapsed=0.000 mem_load_elapsed=5.141 sma_load_elapsed=0.000 composed_elapsed=5.141
*************************** 28.row ***************************
QUERY_PLAN: check_rows=90000
*************************** 29.row ***************************
QUERY_PLAN: -> Data Exchange 1:1 (cost=0.310..0.310 rows=10 width=82)
*************************** 30.row ***************************
QUERY_PLAN: Output: columns=4 width=82
*************************** 31.row ***************************
QUERY_PLAN: Network: mode=concurrent fetch_times=1 fetch_rows=10 fetch_cost=0.251
*************************** 32.row ***************************
QUERY_PLAN: Exec cost: compute=0.002 create=2026-03-13 16:34:44.806485 start=0.308 times=2 input_wait=0.000 output_wait=0.301
*************************** 33.row ***************************
QUERY_PLAN: -> Interval on Column ts (cost=5.316..5.316 rows=10 functions=4 width=82 input_order=asc output_order=asc)
*************************** 34.row ***************************
QUERY_PLAN: Output: columns=4 width=82
*************************** 35.row ***************************
QUERY_PLAN: Time Range: [-9223372036854775808, 9223372036854775807]
*************************** 36.row ***************************
QUERY_PLAN: Time Window: interval=10s offset=0a sliding=10s
*************************** 37.row ***************************
QUERY_PLAN: Merge ResBlocks: False
*************************** 38.row ***************************
QUERY_PLAN: Exec cost: compute=0.258 create=2026-03-13 16:34:44.800435 start=0.020 times=1 input_wait=5.038 output_wait=0.002
*************************** 39.row ***************************
QUERY_PLAN: -> Table Scan on meters (cost=0.303..5.308 rows=100000 columns=2 width=12 order=[asc|1 desc|0] mode=ts_order data_load=sma)
*************************** 40.row ***************************
QUERY_PLAN: Output: columns=2 width=12
*************************** 41.row ***************************
QUERY_PLAN: Time Range: [-9223372036854775808, 9223372036854775807]
*************************** 42.row ***************************
QUERY_PLAN: Exec cost: compute=5.034 create=2026-03-13 16:34:44.800432 start=0.023 times=31 input_wait=0.000 output_wait=0.255
*************************** 43.row ***************************
QUERY_PLAN: -> I/O cost: total_blocks=30 file_load_blocks=0 stt_load_blocks=0 mem_load_blocks=30 sma_load_blocks=0 composed_blocks=30
*************************** 44.row ***************************
QUERY_PLAN: file_load_elapsed=0.000 stt_load_elapsed=0.000 mem_load_elapsed=4.978 sma_load_elapsed=0.000 composed_elapsed=4.978
*************************** 45.row ***************************
QUERY_PLAN: check_rows=100000
*************************** 46.row ***************************
QUERY_PLAN: -> Data Exchange 1:1 (cost=0.579..0.579 rows=10 width=82)
*************************** 47.row ***************************
QUERY_PLAN: Output: columns=4 width=82
*************************** 48.row ***************************
QUERY_PLAN: Network: mode=concurrent fetch_times=1 fetch_rows=10 fetch_cost=0.504
*************************** 49.row ***************************
QUERY_PLAN: Exec cost: compute=0.005 create=2026-03-13 16:34:44.806488 start=0.307 times=2 input_wait=0.267 output_wait=0.029
*************************** 50.row ***************************
QUERY_PLAN: -> Interval on Column ts (cost=4.232..4.232 rows=10 functions=4 width=82 input_order=asc output_order=asc)
*************************** 51.row ***************************
QUERY_PLAN: Output: columns=4 width=82
*************************** 52.row ***************************
QUERY_PLAN: Time Range: [-9223372036854775808, 9223372036854775807]
*************************** 53.row ***************************
QUERY_PLAN: Time Window: interval=10s offset=0a sliding=10s
*************************** 54.row ***************************
QUERY_PLAN: Merge ResBlocks: False
*************************** 55.row ***************************
QUERY_PLAN: Exec cost: compute=0.207 create=2026-03-13 16:34:44.800646 start=0.027 times=1 input_wait=3.998 output_wait=0.003
*************************** 56.row ***************************
QUERY_PLAN: -> Table Scan on meters (cost=0.311..4.224 rows=80000 columns=2 width=12 order=[asc|1 desc|0] mode=ts_order data_load=sma)
*************************** 57.row ***************************
QUERY_PLAN: Output: columns=2 width=12
*************************** 58.row ***************************
QUERY_PLAN: Time Range: [-9223372036854775808, 9223372036854775807]
*************************** 59.row ***************************
QUERY_PLAN: Exec cost: compute=3.996 create=2026-03-13 16:34:44.800642 start=0.032 times=25 input_wait=0.000 output_wait=0.201
*************************** 60.row ***************************
QUERY_PLAN: -> I/O cost: total_blocks=24 file_load_blocks=0 stt_load_blocks=0 mem_load_blocks=24 sma_load_blocks=0 composed_blocks=24
*************************** 61.row ***************************
QUERY_PLAN: file_load_elapsed=0.000 stt_load_elapsed=0.000 mem_load_elapsed=3.922 sma_load_elapsed=0.000 composed_elapsed=3.922
*************************** 62.row ***************************
QUERY_PLAN: check_rows=80000
*************************** 63.row ***************************
QUERY_PLAN: -> Data Exchange 1:1 (cost=0.580..0.580 rows=10 width=82)
*************************** 64.row ***************************
QUERY_PLAN: Output: columns=4 width=82
*************************** 65.row ***************************
QUERY_PLAN: Network: mode=concurrent fetch_times=1 fetch_rows=10 fetch_cost=0.262
*************************** 66.row ***************************
QUERY_PLAN: Exec cost: compute=0.003 create=2026-03-13 16:34:44.806490 start=0.577 times=2 input_wait=0.000 output_wait=0.025
*************************** 67.row ***************************
QUERY_PLAN: -> Interval on Column ts (cost=3.050..3.050 rows=10 functions=4 width=82 input_order=asc output_order=asc)
*************************** 68.row ***************************
QUERY_PLAN: Output: columns=4 width=82
*************************** 69.row ***************************
QUERY_PLAN: Time Range: [-9223372036854775808, 9223372036854775807]
*************************** 70.row ***************************
QUERY_PLAN: Time Window: interval=10s offset=0a sliding=10s
*************************** 71.row ***************************
QUERY_PLAN: Merge ResBlocks: False
*************************** 72.row ***************************
QUERY_PLAN: Exec cost: compute=0.222 create=2026-03-13 16:34:44.800922 start=0.026 times=1 input_wait=2.802 output_wait=0.003
*************************** 73.row ***************************
QUERY_PLAN: -> Table Scan on meters (cost=0.505..3.037 rows=30000 columns=2 width=12 order=[asc|1 desc|0] mode=ts_order data_load=sma)
*************************** 74.row ***************************
QUERY_PLAN: Output: columns=2 width=12
*************************** 75.row ***************************
QUERY_PLAN: Time Range: [-9223372036854775808, 9223372036854775807]
*************************** 76.row ***************************
QUERY_PLAN: Exec cost: compute=2.801 create=2026-03-13 16:34:44.800918 start=0.030 times=10 input_wait=0.000 output_wait=0.212
*************************** 77.row ***************************
QUERY_PLAN: -> I/O cost: total_blocks=9 file_load_blocks=0 stt_load_blocks=0 mem_load_blocks=9 sma_load_blocks=0 composed_blocks=9
*************************** 78.row ***************************
QUERY_PLAN: file_load_elapsed=0.000 stt_load_elapsed=0.000 mem_load_elapsed=2.756 sma_load_elapsed=0.000 composed_elapsed=2.756
*************************** 79.row ***************************
QUERY_PLAN: check_rows=30000
*************************** 80.row ***************************
QUERY_PLAN: Planning Time: 0.484 ms
*************************** 81.row ***************************
QUERY_PLAN: Execution Time: 8.138 ms

诊断建议​

没有使用标签索引​

-> Table Scan on meters (...)
Time Range: [2026-01-01 00:00:00, 2026-01-31 23:59:59]
Tag Index Filter: conditions=location='Beijing' and groupid=2
Primary Filter: ts >= 2026-01-01 00:00:00 and ts < 2026-02-01 00:00:00

重要

  • 在扫描诊断中,Tag Index Filter 是否出现应优先检查;它直接决定是否能在扫描前缩小子表/分片范围,需手动创建
  • 若没有 Tag Index Filter,即使写了标签条件,也可能退化为更大范围扫描,导致 check_rows、file_load_blocks 和整体耗时显著上升

查询慢,但扫描不重​

重点看

  • Sort
  • Group Sort
  • Merge
  • Aggregate
  • Exec cost: compute=...

如果扫描层 I/O cost 不高,而上层 compute 很高,通常瓶颈在计算、排序或归并。

查询慢,而且扫描量大​

重点看

  • Time Range
  • Primary Filter
  • Tag Index Filter
  • check_rows
  • file_load_blocks

如果 Time Range 没有缩小、Tag Index Filter 未出现、check_rows 又很大,往往说明扫描裁剪不充分。

计划里出现很多 Data Exchange​

重点看

  • Network: fetch_cost
  • fetch_times
  • rows=平均值(最大值)
  • slowest_vgroup_id

如果交换层代价高,说明瓶颈可能在跨节点传输,而不是本地算子。

某些节点特别慢​

重点看

  • slowest_vgroup_id
  • slow_deviation
  • cost_ratio
  • data_deviation

判断方法

  • data_deviation 高:更可能是数据倾斜
  • data_deviation 不高但 cost_ratio 高:更可能是某个节点资源异常或热点竞争

过滤条件写了很多,但性能没有改善​

重点看

  • Filter
  • Primary Filter
  • Tag Index Filter
  • Filter: efficiency=...

如果过滤条件只停留在上层算子,而没有出现在扫描层,通常说明还没有真正做到早过滤。

使用建议​

  • 日常查看计划,优先使用 EXPLAIN VERBOSE true
  • 排查慢查询,优先使用 EXPLAIN ANALYZE VERBOSE true
  • 重点观察是否出现 Tag Index Filter、Primary Filter、Data Exchange、Exec cost 和 I/O cost
  • 在多 vgroup 查询中,优先关注所有 平均值(最大值) 形式的字段,它们最容易暴露长尾问题
  • 当 rows、width、fetch_cost 同时偏大时,通常意味着网络传输压力会非常明显

相关文档​