mysql 5.7升8.0后in子查询变慢主因是半连接优化退化:8.0默认启用semijoin=on和materialization=on,但含limit或group by时会退回到物化,而执行计划中select_type和extra含义变化导致误判。

MySQL 5.7 vs 8.0 的子查询执行计划突变
MySQL 8.0 把子查询优化逻辑重写了,EXPLAIN里看到的 select_type 值和 Extra 字段含义都变了。5.7 里常见的 DEPENDENT SUBQUERY 在 8.0 中可能被自动转成 DERIVED 或直接合并进主查询——不是语法变了,是优化器“敢动”了。
典型翻车点:
- 5.7 中
IN (SELECT ...)总是物化为临时表;8.0 默认启用semijoin,但若子查询含LIMIT或GROUP BY,又会退回到物化,而你从 SQL 看不出这个切换逻辑 -
optimizer_switch默认值变了:8.0 开启了materialization=on,semijoin=on,但 5.7 是关的;不显式检查这个变量,就容易误判“为什么同样 SQL 在新库跑得更慢” - 派生表(derived table)在 5.7 不支持下推条件,哪怕你
SELECT id FROM (SELECT * FROM t WHERE x=1) AS dt WHERE id=123,也会先算全表再过滤;8.0 支持条件下推,实际只扫描匹配行
PostgreSQL 12+ 的 GEQO 对多层嵌套 JOIN 的影响
PostgreSQL 在 12 版本起,当嵌套子查询展开后涉及 ≥8 张表 JOIN(即 from_collapse_limit 超限),会默认启用 GEQO(遗传算法优化器)替代传统动态规划。它不保证找到全局最优,但更可能跳出局部陷阱——代价是计划生成时间略长,且结果不可预测。
你遇到的“同一 SQL 在 11 和 13 上执行计划完全不同”,大概率是因为:
- 11 版本强制用 DP 算法,JOIN 顺序固定为语句书写顺序或简单启发式排序
- 13 版本中,即使你没改 SQL,只要统计信息更新导致某张中间表估算行数超过阈值,
GEQO就可能被触发,生成完全不同的连接路径 -
geqo_threshold默认是 12,但嵌套子查询展开后的真实表数量,得看EXPLAIN输出里的Relation Name行数,不是你写的 FROM 个数
Oracle 绑定变量窥探 + 自适应计划 = 执行计划漂移
Oracle 12c 起默认开启自适应执行计划(Adaptive Plans),但它的触发前提是:优化器对某个操作的成本估算偏差超过阈值(比如预估 100 行,实际返回 10000 行),且该操作支持备选路径(如嵌套循环 vs 哈希连接)。嵌套查询恰好是高危区——外层参数影响内层数据量,内层数据量又反向决定外层连接策略。
常见现象:
- 第一次执行带绑定变量的嵌套查询时,优化器“窥探”到一个低频值(如
:status = 'canceled'),生成索引路径;后续传入高频值(:status = 'active'),自适应计划中途切到全表扫描,但你从V$SQL_PLAN看到的是两个不同plan_hash_value -
DBMS_XPLAN.DISPLAY_CURSOR(format => 'adaptive')能显示是否发生了运行时切换,但必须在 SQL 执行完立刻查,否则自适应信息被清理 - 禁用自适应不是好办法:设
OPTIMIZER_ADAPTIVE_FEATURES=FALSE会同时关掉并行分配、统计反馈等关键能力
跨版本对比执行计划前必须确认的三件事
别急着比 EXPLAIN 输出,先同步底层基础。差异往往不出在 SQL 或优化器逻辑,而出在这些“隐形配置”上:
- 统计信息是否一致?用
ANALYZE TABLE或DBMS_STATS.GATHER_TABLE_STATS重新采集,尤其注意直方图开关(method_opt)——MySQL 8.0 直方图默认开,5.7 没这功能 - 优化器参数是否对齐?比如 PostgreSQL 的
enable_hashjoin、join_collapse_limit,Oracle 的_optimizer_cost_model,这些隐藏参数在不同版本默认值可能相反 - 硬件感知是否开启?PolarDB-X 或 MySQL 8.0.33+ 的优化器会读取
innodb_buffer_pool_size和 CPU 核数来调整并行度与内存预算,同一 SQL 在 4 核/16G 和 32 核/128G 环境下,连是否启用哈希连接都可能不同
真正难调的不是“怎么让新版本跑得更快”,而是“怎么让新旧版本行为可预期”——这要求你把统计信息、参数、硬件配置全部纳入版本管理,而不是只盯 SQL 本身。










