presto中join条件应优先放on子句(尤其外连接),where条件用于最终过滤;小表过滤宜用显式join而非in子查询;group by高基数字段置前可防数据倾斜;cte默认不物化,需结合数据量与内存谨慎使用。

WHERE 条件写在 JOIN 里还是外面?
Presto 的执行计划对 JOIN 和 WHERE 的位置极其敏感。把过滤条件放在 ON 子句里(尤其是外连接)可能让结果变错,而放在 WHERE 里又可能拖慢速度——因为 Presto 会先完成 JOIN 再过滤。
- 外连接(
LEFT JOIN)时,ON 中的条件只影响右表匹配逻辑;WHERE 中的条件会对整个结果行过滤,可能把左表本该保留的 NULL 行干掉
- 内连接(
INNER JOIN)下,ON 和 WHERE 效果等价,但 Presto 更倾向把 WHERE 下推到扫描阶段,实际更快
- 如果右表有高基数字段(比如
user_id),在 ON 里加额外条件(如 status = 'active')能显著减少中间数据量
LEFT JOIN)时,ON 中的条件只影响右表匹配逻辑;WHERE 中的条件会对整个结果行过滤,可能把左表本该保留的 NULL 行干掉 INNER JOIN)下,ON 和 WHERE 效果等价,但 Presto 更倾向把 WHERE 下推到扫描阶段,实际更快 user_id),在 ON 里加额外条件(如 status = 'active')能显著减少中间数据量 示例:
SELECT a.id, b.name FROM orders a LEFT JOIN users b ON a.user_id = b.id AND b.status = 'active' -- ✅ 减少右表参与 JOIN 的行数 WHERE a.dt = '2024-06-01'
用 IN 还是 JOIN 做维度表过滤?
当你要用一张小表(比如几十行的 region_map)去筛大表(比如上亿行的 events),别急着写 WHERE country_code IN (SELECT code FROM region_map)。
- Presto 对子查询
IN 支持有限,尤其嵌套深或子查询含聚合时,容易触发 Query exceeded distributed memory limit
- 更稳的方式是显式
JOIN + DISTINCT,让 Presto 能走 broadcast join(小表自动分发到所有 worker)
- 如果小表超过 1MB 或行数超 10 万,Presto 可能放弃 broadcast,转为 shuffle join——这时得手动加
/<em>+ BROADCAST(t) </em>/ 提示
IN 支持有限,尤其嵌套深或子查询含聚合时,容易触发 Query exceeded distributed memory limit JOIN + DISTINCT,让 Presto 能走 broadcast join(小表自动分发到所有 worker) /<em>+ BROADCAST(t) </em>/ 提示 要点:
- 小表必须真正“小”:单条记录别太宽,总大小控制在几百 KB 内
- 确保小表没重复键,否则
JOIN会放大结果行数 - 避免
IN (VALUES (...))超过 1000 项,Presto 解析会变慢
GROUP BY 字段顺序影响性能吗?
不影响正确性,但影响 shuffle 和内存使用。Presto 的 GROUP BY 实际依赖底层的 hash 分组机制,字段顺序决定 hash key 的构成方式。
- 把高基数字段(如
user_id)放前面,低基数字段(如 country)放后面,能让数据更均匀地分散到各 worker,避免 skew
- 反过来,如果先把
country 放第一位,所有中国用户全挤在一个 reducer 上,就容易触发 Query exceeded per-node memory limit
- 如果用了
GROUPING SETS,字段顺序还会影响最终结果集的排序稳定性(虽然 Presto 不保证默认顺序,但物理 layout 会变)
user_id)放前面,低基数字段(如 country)放后面,能让数据更均匀地分散到各 worker,避免 skew country 放第一位,所有中国用户全挤在一个 reducer 上,就容易触发 Query exceeded per-node memory limit GROUPING SETS,字段顺序还会影响最终结果集的排序稳定性(虽然 Presto 不保证默认顺序,但物理 layout 会变) 常见错误现象:
Hishop.5.2.BETA2版主要更新: [修改] 进一步优化了首页打开速度 [修改] 美化了默认模板 [修改] 优化系统架构,程序标签及SQL查询效率,访问系统页面的速度大大提高 [修改] 采用了HTML模板机制,实现了前台模板可视化编辑,降低模板制作与修改的难度. [修改] 全新更换前后台AJAX技术框架,提升了用户操作体验. 店铺管理 [新增] 整合TQ在线客服 [修改] 后台广告位增加
- 查询跑一半报
Exceeded memory limit,但看 profile 发现某个 worker 的 peak memory 是其他 worker 的 5 倍以上 - 加了
EXPLAIN (FORMAT JSON)后发现HashAggregationNode的groupingKeys顺序和直觉相反
CTE(WITH 子句)真能提升可读性?小心物化陷阱
Presto 默认不物化 CTE,也就是每个引用都会重新执行一遍。你以为写一次 WITH base AS (SELECT ...) SELECT * FROM base JOIN ... UNION ALL SELECT * FROM base JOIN ... 是省事,其实是翻倍扫描。
- 没有 hint 的 CTE 就是个语法糖,不是临时表
- 如果 CTE 结果不大(/+ WITH_DATA / 强制物化(Presto 350+ 版本支持)
- 但物化后会占用 worker 内存,多个并发查同一个 CTE 可能互相挤占资源
使用场景判断:
- CTE 里含昂贵计算(如
json_parse()、正则匹配)且被多次引用 → 值得物化 - CTE 只是简单
SELECT ... FROM large_table WHERE dt = 'xxx'→ 别物化,让 Presto 自己下推过滤更高效 - 用
EXPLAIN看执行计划,如果同一个TableScanNode出现两次,说明没复用
复杂点在于:物化与否不能只看代码写法,得结合数据量、集群内存配置、并发压力一起权衡。很多人调完 CTE 发现查询变慢,回头一看是物化吃光了内存,又没开 spill。









