MySQL 查询执行流程的核心阶段是否随版本变化
不会发生根本性变化。从 MySQL 5.6 到 8.0,
SELECT查询的主干流程仍为:连接 → 解析 → 优化 → 执行 → 返回结果。但每个阶段内部的具体实现、默认行为、可选策略和触发条件存在显著差异,尤其在查询优化器和执行引擎层面。
MySQL 5.7 和 8.0 在优化器行为上的关键差异
MySQL 8.0 引入了重构的优化器框架(如
Hypergraph Optimizer实验性支持)、更激进的子查询展开、默认启用
hash_join(5.7 仅支持
BNL或
Block Nested-Loop),且
IN子查询默认转为半连接(
semi-join),而 5.7 需显式开启
optimizer_switch='semijoin=on'。
JOIN算法选择逻辑不同:8.0 中
EXPLAIN显示
Using join buffer (hash join)是常态;5.7 只会显示
Using join buffer (Block Nested Loop)
GROUP BY默认行为变更:8.0 默认按
sql_mode启用
ONLY_FULL_GROUP_BY,5.7 默认关闭,易导致隐式聚合结果不一致
ORDER BY优化退化风险:8.0 对含函数的
ORDER BY(如
ORDER BY UPPER(name))更倾向使用 filesort;5.7 在某些索引覆盖场景下可能避免
执行阶段中容易被忽略的版本相关陷阱
执行计划看似相同,但底层行为可能已变。例如
Handler_read_*状态变量含义未变,但实际调用路径受存储引擎接口演进影响(如 InnoDB 8.0 的
row_search_mvcc调用栈更复杂);又如
information_schema.PROCESSLIST中
STATE字段在 8.0 新增了
Executing hook on transaction begin类状态,5.7 不会出现。 8.0 默认启用
performance_schema且采集粒度更细,开启
events_statements_history_long可能明显拖慢高并发短查询场景,5.7 默认关闭该消费者
PREV_EXEC_TIME在 8.0 的
sys.statement_analysis视图中才开始统计,5.7 无对应字段 8.0 中
KILL QUERY对正在执行
UNION的语句可能中断不彻底(因多线程执行分支),5.7 多为单线程顺序执行,中断更确定
如何验证当前版本的实际执行路径
不能只看
EXPLAIN输出,要结合运行时指标与源码级线索。重点检查三类输出:
SELECT @@version, @@optimizer_switch\G SHOW VARIABLES LIKE 'optimizer_switch'; EXPLAIN FORMAT=TREE SELECT ...; -- 仅 8.0+ 支持
对关键查询,务必开启
optimizer_trace并检查
trace字段中的
steps数组,它会明确写出“尝试了 hash_join”或“跳过 semijoin conversion”等决策依据——这部分内容在 5.7 和 8.0 的 JSON 结构、字段名甚至嵌套深度上都有区别。
真正麻烦的不是流程变了,而是同样一条 SQL,在 5.7 走了索引合并,在 8.0 却因为统计信息采样方式更新(
innodb_stats_persistent_sample_pages默认值从 20 升到 100)而选了全表扫描,且你从
EXPLAIN看不出采样差异来源。
