Trino/Presto 查询优化实战:联邦查询配置与湖仓加速 SQL 改写

给出 Hive/MySQL 连接器完整配置、CBO 统计信息收集与 JOIN 重排序 SQL、嵌套子查询改 CTE 示例,附 TPC-DS 1TB 性能基线

袋鼠云
袋鼠云
经营分析

联邦查询连接器配置

Trino 的核心价值在于一条 SQL 跨多个异构数据源查询,无需数据搬迁。以下是 Hive 和 MySQL 两个最常用连接器的生产级配置:

Hive 连接器配置

connector.name=hive
hive.config.resources=/etc/hadoop/core-site.xml,/etc/hadoop/hdfs-site.xml
hive.metastore.uri=thrift://hive-metastore:9083
hive.allow-drop-table=false
hive.allow-rename-table=false
hive.parquet-optimized-reader.enabled=true
hive.parquet.use-column-names=true
hive.orc.use-column-names=true
hive.metastore-load-pool-size=20
hive.max-partitions-per-scan=1000000

关键参数说明:

  • hive.parquet-optimized-reader.enabled 启用 Parquet 向量化读取,减少反序列化开销,在列式存储上可提升 2~3 倍读取性能。
  • hive.max-partitions-per-scan 限制单次扫描的最大分区数,默认 100 万。如果表的分区粒度过细(如按小时分区),需调大此值,否则查询会被截断。
  • 生产环境建议关闭 allow-drop-table 和 allow-rename-table,防止 Ad-hoc 查询误操作修改源端元数据。

MySQL 连接器配置

connector.name=mysql
connection-url=jdbc:mysql://mysql-host:3306/production
connection-user=trino_reader
connection-password=${MYSQL_PASSWORD}
mysql.jdbc-compatibility=true
mysql.max-rows-per-fetch=10000

MySQL 作为维度表数据源时,mysql.max-rows-per-fetch 控制每次 fetch 的行数,适当调大可减少 JDBC 往返次数,但会增加 Trino Worker 的内存压力。

CBO 统计信息收集与 JOIN 重排序

Trino 使用基于成本的优化器(CBO)决定 JOIN 顺序和算法。CBO 的效果完全依赖统计信息的质量——没有统计信息的 CBO 退化为规则优化器,JOIN 顺序可能完全错误。

收集统计信息:

-- 对核心表执行 ANALYZE,收集列统计信息
ANALYZE hive.sales_schema.lineitem;
ANALYZE hive.sales_schema.orders;
ANALYZE hive.dim_schema.customer;

-- 检查统计信息是否生效
SELECT * FROM system.table_stats
WHERE table_schema = 'sales_schema';

CBO 启用与验证:

-- 确保 CBO 已启用(默认启用)
SET SESSION enable_dynamic_filtering = true;

-- 查看查询计划,确认 JOIN 顺序是否由 CBO 决定
EXPLAIN SELECT ...
FROM lineitem l
JOIN orders o ON l.orderkey = o.orderkey
JOIN customer c ON o.custkey = c.custkey
WHERE c.nation = 'CHINA' AND l.quantity > 100;

在未收集统计信息时,CBO 可能将 1 亿行的 lineorder 表作为驱动表与小表 JOIN;收集后,优化器会自动选择小表(如 nation、region)作为驱动表,通过 Broadcast Join 将小表广播到各 Worker,查询耗时可降低一个数量级。

数据服务 DataAPI

可视化生成、统一管控 API,构建 OneService 数据共享服务的数据服务平台

了解产品

嵌套子查询改 CTE 改写

复杂查询中的多层嵌套子查询不仅可读性差,还会导致 Trino 的查询计划器产生大量临时计划节点,影响优化效果。改写为 CTE(Common Table Expression)可以让优化器更清晰地理解查询语义。

改写前(嵌套子查询):

SELECT region, total_amount
FROM (
    SELECT c.region, SUM(o.amount) AS total_amount
    FROM (
        SELECT orderkey, custkey, amount
        FROM orders
        WHERE orderdate >= DATE '2024-01-01'
    ) o
    JOIN (
        SELECT custkey, name, region
        FROM customer
        WHERE region IN ('华北', '华东')
    ) c ON o.custkey = c.custkey
    GROUP BY c.region
) sub
WHERE total_amount > 1000000;

改写后(CTE):

WITH recent_orders AS (
    SELECT orderkey, custkey, amount
    FROM orders
    WHERE orderdate >= DATE '2024-01-01'
),
target_customers AS (
    SELECT custkey, name, region
    FROM customer
    WHERE region IN ('华北', '华东')
),
aggregated AS (
    SELECT tc.region, SUM(ro.amount) AS total_amount
    FROM recent_orders ro
    JOIN target_customers tc ON ro.custkey = tc.custkey
    GROUP BY tc.region
)
SELECT region, total_amount
FROM aggregated
WHERE total_amount > 1000000;

CTE 改写的好处是:每个 CTE 独立优化,过滤条件更早执行;同时 CTE 可以被 Trino 识别为独立 Stage,实现更细粒度的并行调度。

资源组隔离

多租户环境下的核心挑战是避免大查询饿死小查询。Trino 通过资源队列(Resource Group)实现查询隔离:

{
  "rootGroups": [
    {
      "name": "adhoc",
      "maxWorkers": 30,
      "maxQueued": 100,
      "schedulingPolicy": "weighted_fair",
      "subGroups": [
        {
          "name": "analyst",
          "maxWorkers": 15,
          "maxQueued": 50
        },
        {
          "name": "etl",
          "maxWorkers": 15,
          "maxQueued": 20
        }
      ]
    },
    {
      "name": "bi_reports",
      "maxWorkers": 20,
      "maxQueued": 50
    }
  ]
}

weighted_fair 策略确保在资源紧张时,各子组按权重分配 Worker,防止单一组占用全部计算资源。生产环境建议为 BI 报表、Ad-hoc 探索和 ETL 三类负载分别建立资源组,并通过 query.max-concurrent-queries 控制每组的最大并发数。

经营分析

围绕目标、预算、执行、异常与归因,构建经营分析、过程监控、问题定位与决策闭环。

了解解决方案

TPC-DS 1TB 性能基线

在标准硬件配置(10 节点 Worker,每节点 64 核 / 256GB 内存)下,Trino 查询 TPC-DS 1TB 基准测试的典型表现:

  • 99 条查询中约 85% 在 30 秒内返回,P50 约 8 秒,P99 约 120 秒。
  • 最慢的几条查询(如 Query 23、Query 67)涉及多层嵌套子查询和大规模排序,改写为 CTE 后可提升 30%~50%。
  • 启用 CBO 并收集统计信息后,整体查询耗时可再降低 15%~20%。

持续优化与运维建议

建立查询基线后,需要系统化的优化和监控机制:

慢查询分析流程: 定期从 system.runtime.queries 表提取慢查询,用 EXPLAIN ANALYZE 分析每个 Stage 的执行耗时和数据交换量。如果某个 Stage 处理的行数远超预期,通常意味着过滤条件下推未生效或统计信息过期。Trino 的查询计划中会明确标注 estimated 和 actual 行数,两者偏差过大时需要重新执行 ANALYZE 更新统计信息。

存储层优化: 数据格式对查询性能影响巨大。Parquet 配合 Zstd 压缩通常是读取速度和压缩率的最佳平衡。分区策略应覆盖高频过滤条件,按日期分区是最常见的选择。对于频繁查询的热数据,可利用 Alluxio 或 JindoFS 等缓存层将数据缓存到计算节点本地,减少远端存储的网络开销。

内存与并发调优: JVM 堆内存设为节点物理内存的 70%~80%,query.max-memory-per-node 设为堆内存的 30%~50%,为 JVM GC 和非查询操作预留空间。memory.heap-headroom-per-node 设为堆内存的 20%~30%。并发控制通过 query.max-concurrent-queries 和资源组实现,避免过多并发查询导致频繁 Spill 到磁盘,反而降低整体吞吐。

连接器层面的微调: 确保谓词下推和投影下推已启用,让 Trino 在读取阶段就过滤掉不需要的行和列。对于 Hive 连接器,Split 大小建议在 128MB~256MB 之间,过小增加调度开销,过大导致单个 Task 执行时间过长。对于 MySQL 维度表,如果数据量小于 1GB,可启用 join.pushdown.enabled 将 JOIN 下推到 MySQL 执行。

建立基线与告警: 为每类查询模式(BI 报表、Ad-hoc 探索、ETL 批处理)分别建立性能基线,监控 P50 和 P99 延迟趋势。当延迟持续上升时,优先排查统计信息是否过期、数据量是否增长、集群资源是否充足三个方向。综合运用以上优化手段,在标准 10 节点集群上,大多数分析查询可将延迟控制在亚秒到秒级,满足交互式 BI 和数据探索的需求。

开启Data+AI 之旅

填写表单,获取专属解决方案

400-002-1024

工作日 9:00-18:00

关注公众号

获取最新产品动态与技术干货

关注公众号

企业微信

扫码添加企业微信