bigywater.tech
MySQL / InnoDB · 39

派生表条件下推

用一个先聚合再过滤的派生表查询,对比derived_condition_pushdown开启和关闭时的执行计划和耗时,看条件下推到底省下了什么。

0.0004s
pushdown=on的耗时
0.2554s
pushdown=off的耗时
638倍
的耗时差距,结果完全一致

做的事:一个先聚合再过滤的派生表查询

如果不做任何优化,这类查询"应该"要先把全部数据聚合完,再从聚合结果里挑出需要的那一条。

代码SELECT * FROM ( SELECT customer_id, SUM(amount) AS total FROM orders1 GROUP BY customer_id ) AS agg WHERE agg.customer_id = 500; -- 30万条orders1,按5万个customer_id分组求和

验证:条件下推打开时,只要0.4毫秒

关掉derived_merge,强制这个派生表物化,才能看清楚pushdown做了什么。

结果SET SESSION optimizer_switch='derived_merge=off,derived_condition_pushdown=on'; EXPLAIN SELECT ... -> Index lookup on agg using <auto_key0> (customer_id = 500) -> Materialize -> Filter: (orders1.customer_id = 500) ← 条件被下推到了这一层! -> Index scan on orders1 using idx_cust 耗时: 0.0004 s, 结果: (500, 3398)

关键在 Materialize 下面那一层:Filter: (orders1.customer_id = 500) 是在GROUP BY聚合之前就先筛出customer_id=500的原始订单行,再对这一小撮数据做聚合——条件被"下推"进了派生表内部,而不是等整个派生表都聚合完了再筛选。

验证:关掉pushdown,变成了先聚合全部数据再筛选

同样的查询,只关掉这一个开关。

结果SET SESSION optimizer_switch='derived_merge=off,derived_condition_pushdown=off'; EXPLAIN SELECT ... -> Index lookup on agg using <auto_key0> (customer_id = 500) -> Materialize -> Group aggregate: sum(orders1.amount) ← 先对全部30万行做聚合 -> Index scan on orders1 using idx_cust ← 扫描全部30万行 耗时: 0.2554 s, 结果: (500, 3398) (结果依然正确,只是慢了)
两次耗时差了约638倍(0.0004s vs 0.2554s),返回的结果却完全一样((500, 3398))——这就是"条件下推"带来的收益:逻辑上等价的两种执行顺序,先筛选再聚合,只需要处理30万行里属于customer_id=500的那一小部分(本例只有几十行);先聚合再筛选,则必须先扫描和聚合全部30万行,生成完整的5万条分组结果,最后才从里面挑出1条——多做的那部分聚合工作,全部是白费的。derived_condition_pushdown 默认是开启的,这也是为什么大多数时候用户完全感知不到这个优化在发生,直到某天在一个更老版本或者手动关闭了这个开关的环境里,遇到一条"逻辑很简单但莫名其妙很慢"的查询。

分步演示:条件下推省下的638倍

每一步都是本文上面用EXPLAIN和计时跑出来的原始结果。

条件下推把过滤挪到聚合之前,少做无用功
这一步新验证的设置

    本文用到的工具

    • 本机 MySQL 26.7.0(InnoDB),30万订单聚合成5万客户汇总的派生表
    • optimizer_switch derived_condition_pushdown:对比是否下推的执行计划与耗时
    京ICP备2026067502号-1京公网安备11010502063487号