在 MySQL 5.7 中,视图本质上是一段保存的查询定义。当视图参与多表连接时,优化器会决定是将视图定义合并进主查询,还是先物化成临时表再参与连接。不同的决策会带来数量级的性能差异,而这一切很大程度上受 optimizer_switch 系统变量里的几个开关控制。理解这些开关的行为,是优化视图连接速度的关键一步。

一、MySQL 5.7 视图与优化器开关基础
MySQL 5.7 的优化器通过 optimizer_switch 来开启或关闭特定的优化特性。与视图连接性能最相关的两个开关是 derived_merge 和 materialization。derived_merge 控制是否将派生表(包括视图引用的派生查询)合并到外层查询中;materialization 则控制是否将子查询或派生表物化为临时表。
默认情况下,derived_merge 是开启的。这意味着简单视图往往被直接展开成 SQL 片段,避免临时表开销。但在视图内部包含 GROUP BY、DISTINCT、UNION 等操作时,合并会导致外层连接条件无法下推,优化器被迫生成执行效率极低的计划。此时若手动关闭 derived_merge,强制物化视图结果,反而能利用物化表的索引加速连接。
1.1 查看当前开关状态
可以通过如下语句查看当前会话或全局的 optimizer_switch 配置:
SELECT @@optimizer_switch; -- 输出类似: -- index_merge=on,index_merge_union=on,derived_merge=on,materialization=on,...
如果只想看派生表相关开关,可以用字符串函数过滤,但生产环境建议直接读取后人工比对。了解默认值,是我们做针对性调整的前提。
1.2 视图合并的代价模型
当 derived_merge=on 时,优化器会尝试把视图的 SELECT 揉进主查询。如果视图定义复杂,合并后产生庞大的 JOIN 树,可能导致优化器选错驱动表。相反,关闭该开关后,视图先独立执行并写入临时表(若 materialization=on 则使用物化策略),外层查询直接扫描这张临时表。
这种设计类似于把子查询提前物化,虽然多了写临时表的开销,但切断了错误合并带来的连锁恶化。在报表类、宽表关联场景中,往往物化后的连接速度更快。
二、关键优化器开关详解与调整
要优化视图连接速度,核心就是操纵 derived_merge 与 materialization 的组合。下面给出具体设置语法与适用场景。
2.1 关闭 derived_merge 强制物化
当视图包含聚合或去重,且连接字段有索引时,关闭合并通常有效:
-- 会话级关闭派生表合并 SET SESSION optimizer_switch = 'derived_merge=off'; -- 执行视图连接查询 SELECT a.id, v.total FROM orders a JOIN view_order_stat v ON a.user_id = v.user_id WHERE a.create_date >= '2023-01-01';
上述代码中,view_order_stat 是一个按 user_id 聚合的视图。关闭 derived_merge 后,优化器先算出视图结果并物化,再使用 user_id 索引完成连接。我们曾将类似查询从 3.2 秒降到 0.4 秒。
需要注意的是,关闭派生合并可能增加临时表内存或磁盘占用。若视图结果集巨大,反而拖慢整体。因此调整前后必须用 EXPLAIN 验证。
2.2 配合 materialization 开关
materialization=on 允许优化器将派生表写入临时表。若同时 derived_merge=off,视图基本走物化。若 materialization=off 且 derived_merge=off,某些场景会退化为循环嵌套,性能更差。推荐保持 materialization=on。
-- 保持物化开启,仅关闭合并 SET SESSION optimizer_switch = 'derived_merge=off,materialization=on'; EXPLAIN SELECT * FROM view_user_order vo JOIN payment p ON vo.order_id = p.order_id;
在 EXPLAIN 输出里,若看到 select_type 为 DERIVED,说明视图已物化;若为 SIMPLE 且表列表展开,说明仍被合并。通过对比两者 rows 与 Extra 字段,能直观判断开关效果。
三、基于 EXPLAIN 的验证与对比
任何优化器开关调整都不能盲猜,必须依靠执行计划。下面用一张简表说明不同开关下的典型现象。
| derived_merge | materialization | 视图执行形态 | 适用场景 |
|---|---|---|---|
| on | on | 尽量合并,失败则物化 | 简单视图直连 |
| off | on | 强制物化派生表 | 聚合视图宽表连接 |
| off | off | 禁用物化,易嵌套循环 | 不推荐 |
观察 EXPLAIN 时,重点看 type 列是否为 ref 或 eq_ref,以及 Extra 是否出现 Using temporary。强制物化后,通常出现 Using temporary 但外层连接 type 变好,这就是值得的权衡。
另外,MySQL 5.7 的 EXPLAIN 不支持直接看视图内部,可先用 SHOW CREATE VIEW 取出定义,单独 EXPLAIN 视图本体,确认其自身是否高效,再判断连接层是否需关合并。
3.1 全局与会话级设置建议
不要随意改全局 optimizer_switch,会影响其他业务。应在报表会话或代理层按需设置:
-- 仅当前连接生效 SET optimizer_switch = 'derived_merge=off'; -- 执行慢视图查询后恢复 SET optimizer_switch = 'derived_merge=on';
这种会话级控制能把优化精准限制在问题查询上,避免误伤 OLTP 短事务。若使用中间件,可以把开关封装在 SQL 注释路由规则中。
四、常见误区与补充优化
不少开发者以为视图一定慢,于是把所有视图改回子查询,其实忽略了开关层面的微调。还有人一看到 Using temporary 就恐慌,但在派生表物化场景里,受控的临时表远好于错误合并造成的全表扫。
除开关外,保证视图底层表统计信息新鲜(ANALYZE TABLE)、为连接字段建索引、减少视图内 SELECT *,都能放大开关调整的效果。综合起来,MySQL 5.7 的视图连接优化是一个“开关+索引+统计信息”的系统工程。
调优原则:先 EXPLAIN 定位合并还是物化,再会话级关 derived_merge 验证,最后固化到慢查询专用连接池。
经过上述步骤,原本因优化器过度合并而缓慢的视图连接,通常能获得数倍提升。掌握 optimizer_switch 背后的逻辑,比盲目加索引更节省成本。
MySQL_5.7视图优化optimizer_switch修改时间:2026-08-08 06:54:33