编程 MySQL 9.7 社区版能开 hypergraph_optimizer 了:WL#17265 的改动、五个开关与验证方法

2026-10-22 20:02:09

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 才可能出现明显差异。这一版没有任何公开的基准数据支持“开了就更快”的说法;具体查询是否受益需要自行对比执行计划,必要时再做实测。

相关链接:

复制全文 生成海报 MySQL 数据库 优化器 执行计划 运维

推荐文章

程序员茄子在线接单