2. Trino架构与联邦查询原理
Trino 的联邦查询能力并不是靠一个“万能连接器”实现的,而是由一套清晰的分层执行架构支撑:Coordinator 负责解析、优化与调度,Worker 负责实际读取与计算,Connector 负责把外部数据源抽象成 Trino 能理解的表与分片,Split 是调度的最小数据单元,Stage 则把一条 SQL 拆成可并行执行的阶段。理解这五者的职责边界,是后续做下推优化、数据交换调优和排障的基础。
Coordinator 与 Worker 的职责分工
Coordinator 是集群的“大脑”,它接收客户端提交的 SQL,完成词法/语法解析、语义分析、逻辑计划与分布式物理计划生成,并把执行计划切分成多个 Stage 和 Task,再通过调度器把 Task 分配到 Worker 上。Coordinator 自身通常不承担大规模数据扫描,但在小集群中也可能参与部分任务执行。它同时维护元数据缓存、节点存活状态和查询队列,是查询生命周期、资源组和并发控制的管理者。
Worker 是“手脚”,负责执行被分配的 Task:从 Connector 获取 Split、读取数据页(Page)、执行过滤/聚合/连接等算子,并把中间结果通过 Exchange 传给下游 Stage。Worker 之间是对等关系,没有主从之分,任何一个 Worker 都可以承担任意 Task。生产环境中,Worker 的数量、内存、CPU 和网络带宽直接决定查询的并行度与吞吐上限。
Connector 机制:把异构数据源抽象成表
Connector 是 Trino 与外部系统之间的适配层。每个 Connector 通过 Catalog 注册,向 Trino 暴露三样核心信息:元数据(库、表、列、类型)、数据布局(能否切分、如何切分)以及读写能力(是否支持下推、是否支持写入)。例如 Hive Connector 会把 HDFS/S3 上的文件按分区和文件块切分成 Split;MySQL Connector 则把一张表按主键区间或行数切分成多个 Split。Connector 还负责把 Trino 的谓词、投影、聚合、Limit 等下推给数据源,减少跨网络传输的数据量。
需要强调的是,Connector 只负责“描述数据在哪里、怎么读”,并不决定最终执行计划。是否下推、下推多少,由优化器和 Connector 的能力声明共同决定。这也是为什么同一张 SQL 在不同 Connector 上的性能差异可能很大。
Split 调度与 Stage 执行模型
Split 是 Trino 调度的最小单位,代表一段可独立读取的数据。一个 Stage 会被拆成多个 Task,每个 Task 再消费若干 Split。调度器根据 Split 的大小、数量、节点负载和数据本地性,把 Split 分发给 Worker。Split 的粒度直接影响并行度:Split 太小会导致调度开销和任务碎片化,太大则会造成单任务长尾、并行度不足。
Stage 是执行计划的分布式切分,通常分为三类:源 Stage(Source Stage)直接读取 Connector 数据;中间 Stage 做聚合、连接、重分区;输出 Stage 把结果返回给 Coordinator 和客户端。Stage 之间通过 Exchange 传递数据,Exchange 又分为 Partitioned(按 key 重分布)、Replicated(广播)和 Gather(汇聚到单点)等类型。跨源查询时,不同源 Stage 可能分别读取 Hive、MySQL、Kafka,再在中间 Stage 完成 Join 或 Union,这就是联邦查询的典型形态。
跨源查询下推与数据交换过程
跨源查询的性能关键,在于“能下推的尽量下推,不能下推的才走 Exchange”。以一条同时访问 Hive 订单表和 MySQL 用户表的 SQL 为例:
SELECT u.city, count(*) AS order_cnt
FROM hive.sales.orders o
JOIN mysql.crm.users u ON o.user_id = u.id
WHERE o.dt = '2024-06-01' AND u.status = 'active'
GROUP BY u.city;
执行时,Hive Connector 会把 dt = '2024-06-01' 下推到分区裁剪,只扫描对应分区;MySQL Connector 会把 status = 'active' 下推为 SQL 条件,减少返回行数。两个源 Stage 分别读取过滤后的数据,然后按 user_id 做 Partitioned Exchange,把相同 key 的数据汇聚到同一 Worker 上完成 Join。Join 后的结果再按 city 做局部聚合和最终聚合,最后 Gather 到 Coordinator 返回客户端。
这里有两个容易踩坑的点。第一,如果 Join 的 key 分布严重倾斜,某个 Worker 会承担远超其他节点的数据量,表现为 Stage 长时间卡在 99%。此时可以考虑对 key 加盐打散,或改用 Broadcast Join 把小表广播到所有节点。第二,如果 Connector 不支持某类下推(例如某些 NoSQL 不支持聚合下推),Trino 会把整表数据拉到内存中计算,网络和内存压力会急剧上升。因此上线前应通过 EXPLAIN 查看计划中哪些算子被下推、哪些留在 Trino 侧。
EXPLAIN (TYPE DISTRIBUTED) 输出中的 TableScan 节点:如果过滤条件出现在 TableScan 的 filterPredicate 中,说明已下推;如果出现在上层的 Filter 节点,则说明未下推。生产环境中的关键观察点
理解架构之后,排障就有了抓手。查询变慢时,先看 Coordinator 的查询计划是否合理,再看 Worker 的 Split 处理速率和 Exchange 数据量。如果某个 Stage 的输入行数远大于源表过滤后的行数,说明下推没生效;如果 Exchange 的字节数异常大,说明中间结果没有及时聚合或过滤。集群层面,Worker 的 CPU、堆内存、GC 时间和网络带宽是四个最直接的指标。Coordinator 侧则要关注查询队列长度、并发数和元数据缓存命中率。
最后需要明确:Trino 的联邦查询不是“把数据搬到一起再算”,而是“把计算推到数据附近,只搬运必要的数据”。Coordinator 决定怎么切、怎么分;Worker 决定算得多快;Connector 决定能推多少;Split 和 Stage 决定并行度与数据流向。把这四层关系理清,后续做性能优化时才能定位到真正的瓶颈,而不是盲目加节点或调大内存。