资讯动态

深入解析 TDengine 查询引擎:从 SQL 解析、分布式调度到多级缓存的完整链路

发布时间:2026/9/13 13:49:16 来源:尧图企业网站定制
深入解析 TDengine 查询引擎从 SQL 解析、分布式调度到多级缓存的完整链路【免费下载链接】TDengineHigh-performance, scalable time-series database designed for Industrial IoT (IIoT) scenarios项目地址: https://gitcode.com/GitHub_Trending/tde/TDengineTDengine 作为一个面向工业物联网IIoT场景的高性能时序数据库其查询与计算能力是核心组件之一。本文以 15-internals/03-query.md 为主体结合当前仓库源码完整剖析查询引擎的模块分工、queryPolicy查询策略、时序 SQL 扩展、八步查询执行流程、超级表多表聚合流程以及查询缓存机制。读完本文你将掌握 TDengine 查询任务从客户端到 vnode/qnode 的完整流转过程理解queryPolicy各取值对计算下沉位置的精确影响并能依据源码证据定位查询链路中的关键实现。查询计算中的各模块角色在 TDengine 中一次查询计算任务需要 taosc、vnode、qnode、mnode 四个逻辑单元紧密协作。以一次复杂的超级表聚合查询为例多个 vnode 与多个 qnode 需要共同分担查询与计算职责。vnode、qnode、mnode 的定义与职责可参考 System Architecture。taoscSQL 解析与任务调度入口taosc 是 TDengine 的应用驱动Application Driver负责 SQL 的解析与执行。对于插入型 SQLtaosc 采用流式读取与解析策略来提升处理效率对于其他类型的 SQLtaosc 先通过语法解析器将 SQL 拆解为抽象语法树AST并在解析过程中完成初步的语法校验。一旦发现语法错误taosc 会直接返回错误信息以及错误的具体位置帮助用户快速定位问题。解析得到的 AST 会进一步转换为逻辑查询计划Logical Query Plan逻辑计划经过优化后被转换为物理查询计划Physical Query Plan。随后taosc 的调度器scheduler将物理查询计划转换为查询执行任务并分发给选定的 vnode 或 qnode 执行在收到查询结果就绪的通知后taosc 从相应节点拉取结果并最终返回给用户。在源码层面这一调度逻辑集中在 source/client/src/clientImpl.cscheduleQuery()约 L1370接收查询请求与查询计划 DAG构造SSchedulerReq后调用schedulerExecJob()下发作业异步执行完成后由回调schedulerExecCb()约 L2051收尾客户端阶段仅是调度器作业阶段的回退结果拉取由schedulerFetchRows()约 L3589、L4808完成。可见调度作业执行、回调通知、按需拉取结果正是 taosc 查询执行的核心骨架。mnode元数据服务与心跳维护在 TDengine 集群中超级表信息与元数据库基本信息统一由 mnode 管理。作为元数据服务器mnode 负责响应 taosc 的元数据查询请求当 taosc 需要获取 vgroup 等元数据信息时会向 mnode 发起请求mnode 快速返回所需信息保障 taosc 后续操作顺利推进。此外mnode 还负责接收 taosc 发送的心跳消息。心跳用于维护 taosc 与 mnode 之间的连接状态确保双方通信畅通。从架构上看mnode 最多可配置 3 个通过 Raft 一致性协议保证元数据的一致性详见 System Architecture。vnode数据节点上的虚拟查询单元vnode 是 TDengine 集群中的虚拟节点负责从所属物理节点分发来的任务队列中取出查询请求并执行相应的查询处理。每个 vnode 拥有自己独立的任务队列用于管理与调度查询请求。当 vnode 收到查询请求后会从任务队列中取出并处理处理完成后将查询结果返回给所属物理节点中阻塞查询队列的工作线程或直接返回给 taosc。vnode 同时承担时序数据的存储职责数据按 vgroup 分布、通过一致性哈希确定归属 vnode这一数据分片机制同样见 System Architecture。Executor查询算子与 TSDB 数据读取Executor 模块负责实现各类查询算子Operator。算子通过调用 TSDB 数据读取 API 获取数据内容数据以数据块data block形式返回给 Executor 模块。TSDB 负责从内存或磁盘读取所需信息包括数据块、数据块元数据、数据块统计信息等。TSDB 屏蔽了底层存储层磁盘与内存缓冲的实现细节使 Executor 只需专注于面向列式数据块的查询与处理。这一设计让 Executor 能够高效处理各类查询请求同时简化了数据访问与管理的复杂度。从仓库源码看查询算子种类与上述职责一一对应source/libs/executor/src/ 下包含扫描类算子scanoperator.c数据扫描、cachescanoperator.c缓存扫描、rowsetscanoperator.c、virtualtablescanoperator.c、sysscanoperator.c、federatedscanoperator.c窗口类算子timewindowoperator.c时间窗口、countwindowoperator.c计数窗口、eventwindowoperator.c事件窗口、externalwindowoperator.c、anomalywindowoperator.c聚合与计算类算子aggregateoperator.c聚合、groupoperator.c分组、projectoperator.c投影、filloperator.c填充、sortoperator.c排序、mergeoperator.c合并、exchangeoperator.c数据交换连接类算子hashjoinoperator.c、mergejoinoperator.c。这些算子的正确性由 source/libs/executor/test/ 下的单元测试覆盖例如executorTests.cpp、queryPlanTests.cpp、timewindowTest.cpp、windowFuncFrameTests.cpp、joinTests.cpp、sortTests.cpp可作为理解算子行为的第一手验证材料。UDF Daemon用户自定义函数的独立计算组件在分布式数据库系统中执行 UDF用户自定义函数的计算节点负责处理涉及 UDF 的查询。当查询使用 UDF 时查询模块负责调度 UDF Daemon 执行 UDF 计算并取回结果。UDF Daemon 是一个独立的计算组件负责执行用户自定义函数可处理时序数据与表格式数据等多种数据类型。通过将 UDF 计算任务分发到 UDF Daemon查询模块能够将计算负载与主查询处理流程分离从而提升系统的整体性能与可扩展性。UDF 执行期间查询模块与 UDF Daemon 紧密协作确保计算任务的正确执行与结果的及时返回。查询策略queryPolicy 配置详解为满足不同场景需求TDengine 集群提供了查询策略配置项queryPolicy允许用户按需选择查询执行框架。该配置项位于 taosc 配置文件中且每个配置项只对单个 taosc 生效因此集群内不同 taosc 可以混用不同策略。queryPolicy的取值与含义如下取值含义1所有查询仅使用 vnode默认值2混合使用 vnode/qnode混合模式无扫描算子的子任务在 qnode 执行含扫描算子的子任务在 vnode 执行3除表扫描功能使用 vnode 外其他查询计算功能仅使用 qnode即 vnode 只运行扫描算子4使用客户端聚合模式通过选择合适的查询策略用户可以在不同节点间灵活分配与控制查询资源实现存算分离、追求极致性能等目标。该配置项在源码中的注册位置为 source/common/src/tglobal.ccfgAddInt32(pCfg, queryPolicy, tsQueryPolicy, 1, 4, CFG_SCOPE_CLIENT, CFG_DYN_ENT_CLIENT, ...)这条注册语句明确了三件事取值范围 1–4与文档表格一一对应作用域为客户端CFG_SCOPE_CLIENT印证了每个配置项只对单个 taosc 生效的说明支持动态修改CFG_DYN_ENT_CLIENT即运行期修改无需重启即可生效。该配置项的全局变量为tsQueryPolicy见 tglobal.c 的配置项映射表。更多与查询相关的客户端参数如countAlwaysReturnValue、keepColumnName、metaCacheMaxSize、querySmaOptimize、queryTableNotExistAsEmpty等可参考 taosc Reference 中的 Query Related 参数表其中对queryPolicy的语义描述与本文表格完全一致。关于 qnode 的集群级行为test/cases/uncatalog/system-test/2-query/test_qnodeCluster.py 提供了端到端测试参考测试在多个 dnode 上创建 mnode 与 qnodecreate qnode on dnode 1/2/3随后执行超级表聚合查询验证结果可视为queryPolicy混合模式下的真实集群验证样例。SQL 扩展为时序场景定制的查询语法TDengine 采用 SQL 作为查询语言大幅降低了学习门槛。在标准 SQL 基础上TDengine 针对时序数据库的独特查询需求做了多项扩展完整语法细节可参考 Time-Series Extensions。分组功能扩展partition byTDengine 扩展了标准 SQL 的分组功能引入partition by子句。用户可基于自定义维度切分输入数据并在每个分组内执行任意形式的查询操作常量、聚合、标量、表达式等。例如按location标签分组求平均电压select location, avg(voltage) from meters partition by location其中最典型的是partition by tbname用法——它隔离出每个子表的数据形成独立时间序列例如select _wstart, tbname, avg(voltage) from meters partition by tbname interval(10m)Limit 功能扩展slimit/soffset对于需要限制分组输出数量的分组查询TDengine 引入slimit与soffset子句限制分组数量。当limit与partition by子句联用时其含义变为组内限制而非全局限制。在 EXPLAIN 输出说明 中slimit与soffset字段即对应当前算子看到的SLIMIT与SOFFSET用于判断分组输出数量是否被提前约束。标签查询支持标签作为子表属性在查询中可作为伪列使用。对于只查询标签列、不关心时序数据的场景TDengine 引入tag关键字加速查询避免扫描时序数据。窗口查询支持TDengine 支持多种窗口查询包括时间窗口、状态窗口、会话窗口、事件窗口、计数窗口等未来还将支持更灵活的用户自定义窗口。窗口子句的完整语法如下摘自 Time-Series Extensionswindow_clause: { SESSION(ts_col, tol_val) | STATE_WINDOW(state_expr [, state_expr ...]) [EXTEND(extend_val)] [ZEROTH_STATE(zeroth_val [, zeroth_val ...])] [TRUE_FOR(true_for_expr)] | INTERVAL(interval_val [, interval_offset]) [SLIDING (sliding_val)] [fill_clause] | EXTERNAL_WINDOW ((subquery) window_alias) [fill_clause] | EVENT_WINDOW START WITH start_trigger_condition END WITH end_trigger_condition [TRUE_FOR(true_for_expr)] | COUNT_WINDOW(count_val[, sliding_val][, col_name ...]) }其中interval_val与sliding_val均表示时间段interval_offset表示窗口偏移必须小于interval_val语法支持无单位使用库的时间精度、单字符单位如INTERVAL(1s, 500a) SLIDING(1s)与字符串单位如INTERVAL(1s, 500a)三种形式。上述各类窗口在 Executor 中均有对应算子实现即 source/libs/executor/src/ 下的timewindowoperator.c、statewindow含eventwindowoperator.c、countwindowoperator.c、externalwindowoperator.c等。连接查询扩展除传统的 Inner、Outer、Semi、Anti-Semi Join 外TDengine 还支持时序数据库特有的 ASOF Join 与 Window Join使用户可以更便捷灵活地完成关联查询。连接算子的实现位于 source/libs/executor/src/ 的hashjoinoperator.c与mergejoinoperator.c。此外自 v3.4.2.0 起 TDengine 支持 SQL 标准的OVER子句与窗口函数moving average、running total、分区排名等分析场景与上述时间窗口聚合INTERVAL等将多行聚合为单行输出不同窗口函数保留每一行并追加计算列详见 Window Functions。查询执行流程从 SQL 到结果集的八个步骤TDengine 的完整查询执行流程可分为八个步骤Step 1taosc 解析 SQL 并生成 AST。Catalog 模块按需向 vnode 或 mnode 请求查询涉及表的元数据信息并基于元数据执行权限检查、语法校验与合法性校验。Step 2合法性校验完成后生成逻辑查询计划。所有优化策略按顺序执行对执行计划进行扫描与重写再基于 vgroup 数量与 qnode 数量的元数据信息由逻辑查询计划生成对应的物理查询计划。Step 3客户端查询调度器开始任务调度。根据数据亲和性data affinity或负载信息将查询子任务分发给某个 vnode 或 qnode 所属的 dnode。Step 4dnode 收到查询任务后识别目标 vnode 或 qnode并将消息转发至对应节点的查询执行队列。Step 5vnode 或 qnode 的查询执行线程从查询队列取出任务信息建立基础查询执行环境并立即执行查询获得部分可用查询结果后通知客户端调度器。Step 6客户端调度器按执行计划完成全部任务的调度。在用户 API 驱动下向最顶层算子所在的查询执行节点发送取数请求读取数据请求结果。Step 7各算子根据父子关系从下游算子获取并向上层返回数据。Step 8taosc 将拉取到的全部查询结果返回给上层应用。对照源码Step 3–6 的调度与取数逻辑体现在 source/client/src/clientImpl.c 的scheduleQuery()/schedulerExecJob()/schedulerExecCb()/schedulerFetchRows()调用链中Step 5–7 的算子级执行则由 source/libs/executor/src/ 中的各类算子完成算子间通过父子关系parent-child组织执行计划 DAG这与 Step 7 的描述一致。多表聚合查询超级表上的分布式聚合实际应用中常需要高效聚合不同采集点的数据为此 TDengine 引入超级表supertable概念。超级表是一种特殊的表结构代表一类数据模式相同的采集点集合本质上它是若干具有相同字段定义、但拥有唯一静态标签的表集合标签可以有多个且可随时增删改。通过超级表应用可以指定标签过滤条件对超级表下全部或部分表执行聚合或统计操作极大简化应用开发过程。超级表多表聚合查询的完整流程如下具体步骤为Step 1taosc 从 mnode 获取数据库与表的元数据信息。Step 2mnode 返回请求的元数据信息。Step 3taosc 向超级表所属的每个 vnode 发送查询请求。Step 4vnode 发起本地查询获得结果后返回查询响应。Step 5taosc 向聚合节点此处为 qnode发送查询请求。Step 6qnode 向各 vnode 发送数据请求消息以拉取数据。Step 7vnode 返回本节点的查询结果。Step 8qnode 完成多节点数据聚合将最终查询结果返回客户端。为提升聚合计算速度TDengine 在 vnode 内实现了标签数据与时序数据的分离存储系统首先在内存中过滤标签数据确定参与聚合操作的表集合从而大幅缩减需要扫描的数据集显著提升聚合计算速度。此外得益于数据分布在多个 vnode 上聚合操作可在多个 vnode 间并发执行这种分布式处理方式进一步加速了聚合使 TDengine 能够更高效地处理大规模时序数据。需要说明的是普通表上的聚合查询及大多数操作同样适用于超级表且语法完全相同详见 TDengine SQL 手册。聚合正确性的端到端验证可参考 test_qnodeCluster.py 中的超级表建表、写数、聚合查询与结果比对流程。查询缓存多级缓存类型与管理方案缓存技术在 TDengine 的查询与计算全过程中扮演关键角色数据存储、查询优化、执行计划生成、数据检索等阶段都充分使用缓存。通过缓存热点数据与计算结果TDengine 显著减少对底层存储系统的访问次数、降低计算开销从而提升整体查询计算效率。同时缓存机制具备智能性能根据数据访问模式与系统负载动态调整缓存策略在复杂多变的查询负载下维持良好性能。缓存的数据类型TDengine 缓存的数据类型包括元数据Metadata数据库、表元数据、stable vgroup连接数据Connection datarpc session、http session时序数据Time-series databuffer pool、多级存储最新数据Latest datalast、last_row。缓存管理方案TDengine 对不同类型缓存对象采用相应管理策略。对于元数据、RPC 对象与查询对象TDengine 使用哈希缓存管理方法通过一个列表统一管理列表中的每个元素都是一个缓存结构包含缓存信息、哈希表、垃圾回收链表、统计信息、锁与刷新频率。为确保缓存有效性与系统性能TDengine 还通过刷新线程定期检查缓存列表中的过期数据并删除——这种定期清理机制避免缓存中堆积过多无用数据既降低系统资源消耗又维持缓存数据的时效性与准确性。缓存方案如下图所示元数据缓存meta data包含数据库、超级表、用户、节点、视图、虚拟节点以及表的 schema 及其与虚拟节点的映射关系等信息。在 taosc 中缓存元数据可避免频繁向 mnode/vnode 请求元数据。taosc 为元数据使用固定大小的缓存空间先到先得直到缓存空间耗尽缓存空间耗尽后会进行部分逐出eviction为后续新请求所需的元数据腾出空间。缓存大小可通过客户端参数metaCacheMaxSize单客户端元数据缓存上限单位 MB默认 -1 表示不限控制见 taosc Reference。时序数据缓存time-series data时序数据首先以 SkipList 形式缓存在 vnode 内存中当满足落盘条件时时序数据被压缩写入数据存储文件并从缓存中清除。缓存内存大小由数据库参数buffer控制具体机制见 System Architecture 的Time-Series Data Cache小节。最新数据缓存last/last_row缓存时序数据中的最新数据可提升最新数据的查询效率。最新数据按子表组织为 KV 形式其中 K 为子表 IDV 为该子表各列的最后一个非 NULL 且最新的数据行。其启用与否由数据库参数cachemodel控制none/last_row/last_value/both并可通过SHOW VGROUPS的cacheload列查看缓存内存占用详见 System Architecture 的last/last_row Cache小节。小结TDengine 查询引擎的设计要点可以归纳为四条主线分层协作taosc 解析调度、mnode 提供元数据、vnode 存储与本地查询、qnode 聚合计算、UDF Daemon 承载自定义计算、策略可配queryPolicy1–4 决定计算下沉位置支持存算分离、语法贴合时序partition by、slimit/soffset、标签查询、多类窗口与 ASOF/Window Join、缓存多级覆盖元数据、连接、时序数据与最新数据各有专属缓存策略。无论你是要调优查询性能、排查查询链路问题还是扩展新的查询算子都可以从本文提到的源码路径clientImpl.c、executor/src、tglobal.c与文档路径taosc Reference、Time-Series Extensions、System Architecture入手进一步深入钻研。【免费下载链接】TDengineHigh-performance, scalable time-series database designed for Industrial IoT (IIoT) scenarios项目地址: https://gitcode.com/GitHub_Trending/tde/TDengine创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考

读完文章,也想定制专属网站?

尧图设计师 24 小时内与您沟通定制方案

免费获取报价