MySQL 9.7 社区版能开 hypergraph_optimizer 了:WL#17265 的改动、五个开关与验证方法
现象:flag 在,但开不起来
在此之前,社区版 MySQL 的 optimizer_switch 里已经能看到 hypergraph_optimizer 这个 flag,但服务端并没有编入对应支持。于是无论走 SET optimizer_switch、SET GLOBAL、SET PERSIST、--optimizer-switch 还是 SET_VAR hint,结果要么直接失败,要么被静默忽略,具体取决于用哪种机制。
WL#17265(Status: Complete,Server-9.x)改的就是这件事:让社区构建默认编入 hypergraph optimizer,并允许在运行时启用。注意“编入”和“默认开启”是两码事——默认运行时行为不变,会话里依然是关闭的。
WL#17265 到底改了什么
一句话:改构建配置,不改默认行为。
WITH_HYPERGRAPH_OPTIMIZER(默认ON):控制 hypergraph optimizer 是否被编译进来,因而决定它能不能在运行时被启用。ENABLE_HYPERGRAPH_OPTIMIZER(默认OFF):控制它是否出现在OPTIMIZER_SWITCH_DEFAULT里,也就是是否默认开启。- 若显式指定
ENABLE_HYPERGRAPH_OPTIMIZER=ON,则WITH_HYPERGRAPH_OPTIMIZER一律按ON处理——即使同一次 CMake 调用里显式写了WITH_HYPERGRAPH_OPTIMIZER=OFF。
NFR1 明确要求默认运行时行为不变:WITH_HYPERGRAPH_OPTIMIZER=ON 且 ENABLE_HYPERGRAPH_OPTIMIZER=OFF(默认组合)时,除非用户主动通过 optimizer_switch 打开,服务端行为和查询计划都不受影响。
上游提交(mysql/mysql-server,8b2bcb9,2026-02-13):
Make the hypergraph optimizer available by default. It is still disabled by default, and has to be enabled by setting the optimizer switch hypergraph_optimizer=on in order to be used.
MySQL 9.7.0 发行说明(2026-04-21):
The Hypergraph Optimizer is now available in MySQL Community Edition.
五种开启方式与作用域
没有引入新 SQL 语法,全部复用现有的 optimizer_switch 机制:
| 作用域 | 写法 |
|---|---|
| session | `SET optimizer_switch='hypergraph_optimizer=on |
| global | `SET GLOBAL optimizer_switch='hypergraph_optimizer=on |
| persisted | `SET PERSIST optimizer_switch='hypergraph_optimizer=on |
| 启动参数 | `mysqld --optimizer-switch='hypergraph_optimizer=on |
| 单语句 hint | `/*+ SET_VAR(optimizer_switch='hypergraph_optimizer=on |
其中几条容易踩的行为边界:
SET PERSIST不会影响当前会话的@@session.optimizer_switch,只有重启后@@global.optimizer_switch才会体现hypergraph_optimizer=on。- 用
--optimizer-switch启动后,要开一个新连接再看@@optimizer_switch/@@global.optimizer_switch,别在旧会话里判断。 - 给
optimizer_switch赋值时,没有提到的 flag 保持当前值不变,所以只写hypergraph_optimizer=on不会把别的开关重置掉。
查看当前值:
SELECT @@optimizer_switch;
SELECT @@global.optimizer_switch;
SELECT @@optimizer_switch\G
\G 展开后的输出形如:
..., prefer_ordering_index=on,hypergraph_optimizer=off,derived_condition_pushdown=on,hash_set_operations=on
编译关闭时:三种表现
当构建时是 WITH_HYPERGRAPH_OPTIMIZER=OFF,不同入口的表现并不一致,这也是最容易误判“已经打开了”的地方。
一、会话/全局/持久化 SET 直接报错
SET optimizer_switch='hypergraph_optimizer=on';
SET GLOBAL optimizer_switch='hypergraph_optimizer=on';
SET PERSIST optimizer_switch='hypergraph_optimizer=on';
这三条都会失败,错误码 ER_HYPERGRAPH_NOT_SUPPORTED_YET(3999)。错误信息会指出是因为 WITH_HYPERGRAPH_OPTIMIZER=OFF 导致不支持,并提示需要以 CMake 选项 WITH_HYPERGRAPH_OPTIMIZER=ON 重新构建。
二、启动参数被接受,但不生效
mysqld --optimizer-switch=hypergraph_optimizer=on
服务端能正常启动,参数被接受。但启动后 SELECT @@optimizer_switch; 和 SELECT @@global.optimizer_switch; 里显示的都是 hypergraph_optimizer=off。此时再执行 SET optimizer_switch 会按上面第一条失败。
这正是 worklog 里刻意区分“启动成功”和“设置生效”的原因:--optimizer-switch 不合法不报错,不代表开关生效了。
三、SET_VAR hint 被容忍,产生 3999 警告
SELECT /*+ SET_VAR(optimizer_switch='hypergraph_optimizer=on') */ ...;
语句照常执行成功,但 hypergraph optimizer 不会被使用,服务端针对该语句抛出 ER_HYPERGRAPH_NOT_SUPPORTED_YET(3999)的 warning。要确认这一点,请检查 SHOW WARNINGS,而不只是看语句有没有返回结果。
怎么证明真的用上了:optimizer trace
SELECT @@optimizer_switch 只能说明 flag 是 on;至于这条语句在优化时到底走的哪套优化器,worklog 给出的验证途径是 optimizer trace。
SET optimizer_trace='enabled=on';
-- 执行待验证的语句
SELECT * FROM information_schema.OPTIMIZER_TRACE;
SET optimizer_trace='enabled=off';
对比同一个语句的两次执行:一次不加 hint 走默认 (非 hypergraph),一次带 /*+ SET_VAR(optimizer_switch='hypergraph_optimizer=on') */。因为 SQL、schema、数据完全一致,两次唯一差别就是优化器,可以直接把两份 trace 里的计划搜索部分并排放。trace 的具体字段名以你使用的 9.7 版本实际输出为准,请自行对比确认,不要只凭 @@optimizer_switch 判断。
原理与取舍:左深树 vs hypergraph 动态规划
MySQL 传统优化器用左深树(left-deep tree)确定 join order,实现简单、搜索空间小,简单查询够用;但表一多,很多更优的执行计划根本不在它的搜索范围内。
hypergraph optimizer 把整个查询建模成 hypergraph,用动态规划在更大的计划空间里搜索。代价是优化阶段本身的开销更高,收益则取决于查询的连接形态——多表复杂 join 才可能出现明显差异。这一版没有任何公开的基准数据支持“开了就更快”的说法;具体查询是否受益需要自行对比执行计划,必要时再做实测。
相关链接: