tencent cloud

云数据库 MySQL

动态与公告
产品动态
产品公告
新手指引
产品简介
产品概述
产品优势
应用场景
数据库架构
隔离策略
经济型实例
产品功能列表
数据库实例
高可用性(多可用区)
地域和可用区
自研内核
内核概述
内核版本更新动态
功能类特性
性能类特性
安全类特性
稳定类特性
TXRocks 引擎
内核问题检查与修复
购买指南
计费概述
选型指南
购买方式
续费说明
欠费说明
退费说明
按量转包年包月
调整实例费用说明
备份空间收费说明
数据库审计计费说明
数据库代理商业化计费和活动说明
数据库代理计费周期说明
查看费用账单
快速入门
入门概述
创建 MySQL 实例
连接 MySQL 实例
SQL 洞察(原数据库审计)
数据库审计简介
查看审计实例列表
开通审计服务
查看审计日志
日志投递
配置事后告警
修改审计规则
修改审计服务
关闭审计服务
审计规则模板
SQL 审计规则(旧版)
查看审计任务
授权子用户使用数据库审计
MySQL 云盘版
MySQL 云盘版概述
创建 MySQL 云盘版实例
维护管理实例
查看实例监控
调整实例配置
其他功能相关操作
迁移或升级至 MySQL 云盘版
操作指南
使用限制
操作总览
维护管理实例
升级实例
CPU 弹性扩容
只读/灾备实例
数据库代理
数据库管理(DMC)
账号管理
参数配置
备份与回档
数据迁移
网络与安全
监控与告警
日志中心
只读分析引擎
标签
实践教程
腾讯云 MySQL 帮助实现 MySQL 5.7升级至 MySQL 8.0
MySQL 5.6升级到 MySQL 5.7方法和说明
云数据库 MySQL 数据库审计等保实践
构建全场景高可用架构
云数据库 MySQL 使用规范
应用程序配置自动重连功能
MySQL 主实例参数修改的影响
MyISAM 自动转换为 InnoDB 引擎限制
为云数据库 MySQL 创建 VPC
使用云数据库 MySQL 提高业务负载能力
两地三中心灾备建设
读写分离扩展云数据库 MySQL 性能
使用 DTS 将 InnoDB 数据迁移至 RocksDB
构建 LAMP 堆栈 Web 应用程序
构建 Drupal 网站
通过 Python 语言使用 MySQL API
主备实例查询数据不一致
白皮书
性能白皮书
安全白皮书
故障处理
连接相关
性能相关
实例数据同步延迟
设置大小写不敏感失败
通过命令获取 slow_query_log_file 失败
API 文档
History
Introduction
API Category
Instance APIs
调用方式
Data Import APIs
Database Proxy APIs
数据库审计相关接口
Security APIs
Task APIs
Backup APIs
Account APIs
Rollback APIs
Parameter APIs
Database APIs
Monitoring APIs
Log-related API
Data Types
Error Codes
常见问题
选型相关
计费相关
备份相关
回档相关
连接登录
参数修改
升级相关
账号权限
性能内存
运维相关
数据迁移
功能特性
控制台相关
日志相关
事件相关
数据库审计
实例切换影响
API 2.0 切换 3.0 指引
相关协议
服务等级协议
服务条款
通用参考
标准与认证
联系我们
词汇表

通过 HINT 优化 SQL 执行

PDF
聚焦模式
字号
最后更新时间: 2025-05-09 11:51:24

什么是 Hint

优化器一般会为用户 SQL 选择最优的执行计划,但是在某些场景下,例如统计信息估计误差、代价模型拟合偏差等,优化器生成的执行计划可能就不是最优的。这个时候用户可以通过 Hint 机制指导优化器生成更优的执行计划。
Hints 不区分大小写,通过 /*+ ... */ 注释的形式跟在 SELECT 关键字后面,多个 Hint 通过空格或逗号间隔开。以下是一个 Hint 使用示例。
SELECT /*+ [hint_text] [hin_text]... */ * FROM ....

Hint 生效范围

Hint 以 Query Block 为单位生效,在查询语句中,每一个查询 Block 都会有一个 QB_NAME (Query Block Name),只读分析引擎会按照 @sel_1、@sel_2 的方式,从左到右依次为每个查询 Block 生成 QB_NAME。以下面的 SQL 为例。
SELECT * FROM (SELECT * FROM t) t1, (SELECT * FROM t) t2;
这条 SQL 中包含了三个 Query Block,最外面一层 SELECT 所在的查询块的名字为 sel_1,两个 SELECT 子查询的名字依次为 sel_2 和 sel_3,编号依次递增。Hint 中使用 QB_NAME 即可控制 Hint 的作用范围以及 Hint 的作用对象。如果在 Hint 中没有显式指定 QB_NAME,则 Hint 作用范围为当前 Hint 所在的 Query Block,示例如下。
SELECT /*+ HASH_JOIN_PROBE(@sel_2 t1) */ * FROM (SELECT t1.a, t1.b FROM t t1, t t2 WHERE t1.a = t2.a) t1, t t3 WHERE t1.b = t3.b;
SELECT /*+ HASH_JOIN_PROBE(t1@sel_2) */ * FROM (SELECT t1.a, t1.b FROM t t1, t t2 WHERE t1.a = t2.a) t1, t t3 WHERE t1.b = t3.b;
以上两条 SQL 使用了两种不同方法在 Hint 中指定 QB_NAME,第一条 SQL 通过在 Hint 的第一个参数指定 QB_NAME 并用空格与其他参数间隔开。第二条 SQL 通过在参数中后面加上 QB_NAME 来指定 Hint 的生效范围。

支持的 Hint 概览

只读分析引擎 Hint 相关参数名称、语义和语法如下表所示。
名称
语法
语义
SHUFFLE_JOIN
SHUFFLE_JOIN([QB_NAME] tbl1_name,tbl2_name …)
指定 JOIN 操作采用 shuffle 方式分发数据。
BROADCAST_JOIN
BROADCAST_JOIN([QB_NAME] tbl1_name,tbl2_name …)
指定 JOIN 操作采用 broadcast 方式分发数据。
HASH_JOIN_BUILD
HASH_JOIN_BUILD([QB_NAME] tbl1_name,tbl2_name …)
指定 HASH JOIN 操作中的 Build 表。
HASH_JOIN_PROBE
HASH_JOIN_PROBE([QB_NAME] tbl1_name,tbl2_name …)
指定 HASH JOIN 操作中的 Probe 表。
LEADING
LEADING([QB_NAME] tbl1_name,tbl2_name …)
指定 JOIN 操作的 Join Order。
SET_VAR
SET_VAR(setting_name = value)
在 SQL 级别设置系统参数。
NO_PX_JOIN_FILTER_ID/PX_JOIN_FILTER_ID
NO_PX_JOIN_FILTER_ID(rf_id1,rf_id2…)/
PX_JOIN_FILTER_ID(rf_id1,rf_id2…)
控制 runtime filter 的开启与关闭。

只读分析引擎 Hint 语法详解

SHUFFLE_JOIN(t1_name, t2_name ...)

使用说明
SHUFFLE_JOIN(t1_name, t2_name ...),用于控制只读分析引擎优化器在进行 JOIN 操作的时候采用 Shuffle Join 算法,将左右两个表数据进行打散重新分布,再进行 JOIN 操作返回结果。
参考示例
EXPLAIN SELECT /*+ SHUFFLE_JOIN(t1) */ * FROM t1, t2 WHERE t1.id = t2.id;
此条 SQL 中 t1 与 t2 表进行 JOIN 操作,通过 SHUFFLE_JOIN Hint 指定了 Join 数据分发方式为 Shuffle Join。最终计划如下图所示,可以看到 Details 中的第2行和第5行的 EXCHANGE TYPE 已经变成了 HASH,代表采用了 Hash Shuffle。

除了指定单表外,还可以指定 JOIN 中间结果进行数据重分布。
EXPLAIN SELECT /*+ SHUFFLE_JOIN((t1@sel_2,t2@sel_2)) */ * FROM (SELECT t1.a, t1.b FROM t t1, t t2 WHERE t1.a = t2.a) t1, t t3 WHERE t1.b = t3.b;
如上所示,通过将 t1 表与 t3 表用括号括起来,并指定好每个表的 QB_NAME,即可指定 t1 与 t3 表 JOIN 的中间结果与 t2 表 JOIN 时采用 Shuffle Join。

注意:
此 Hint 只在生成分布式计划时生效,在单机计划时无效。

BROADCAST_JOIN(t1_name [, tl_name ...])

使用说明
BROADCAST_JOIN(t1_name,t2_name...),用于控制只读分析引擎优化器在进行 JOIN 操作时,采用 Broadcast Join 算法,将指定表数据广播到所有节点上,进行 JOIN 操作并返回结果。
参考示例
EXPLAIN SELECT /*+ BROADCAST_JOIN(t1) */ * FROM t1, t2 WHERE t1.id = t2.id;
此条 SQL 中 t1 与 t2 表进行 JOIN 操作,通过 BROADCAST_JOIN Hint 指定了 JOIN 方式为 Broadcast Join。最终计划如下图所示,可以看到 Details 中的第2行和第5行的 EXCHANGE TYPE 已经变成了 BCJ,代表采用了 Broadcast。

除了指定单表外,还可以指定 JOIN 中间结果进行数据广播。
EXPLAIN SELECT /*+ BROADCAST_JOIN((t1@sel_2,t3@sel_1)) */ * FROM (SELECT t1.a, t1.b FROM t t1, t t2 WHERE t1.a = t2.a) t1, t t3 WHERE t1.b = t3.b;
如上所示,通过将 t1 表与 t3 表用括号括起来,并指定好每个表的 QB_NAME 即可指定 t1 与 t3 表 JOIN 的中间结果与 t2 表 JOIN 时采用 Broadcast Join,结果如下图所示。

注意:
此 Hint 只在生成分布式计划时生效,在单机计划时无效。
只读分析引擎优化器会选择 Hash Join 的 Build 端作为 Broadcast 的广播表,若需要进行调整,可结合 HASH_JOIN_BUILD 一起进行调整。

HASH_JOIN_BUILD(t1_name,t2_name…)

使用说明
HASH_JOIN_BUILD(t1_name,t2_name ...),用于控制只读分析引擎优化器对指定表使用 HASH JOIN 算法,同时将指定表作为 HASH JOIN 算法的 Build 端,即用指定表来构建哈希表。
参考示例
EXPLAIN SELECT /*+ HASH_JOIN_BUILD(t2)*/ * FROM t t1, t t2 WHERE t1.a = t2.a;
此条 SQL 指定了 t2 表作为 HASH JOIN 中的 Build 表,最终计划如下图所示。

除了指定单个表外,还可以指定 JOIN 中间结果作为 BUILD 端,示例如下。
EXPLAIN SELECT /*+ HASH_JOIN_BUILD((t1@sel_2,t3@sel_1)) */ * FROM (SELECT t1.a, t1.b FROM t t1, t t2 WHERE t1.a = t2.a) t1, t t3 WHERE t1.b = t3.b;
如上所示,通过将 t1 表与 t2 表用括号括起来,并指定好每个表的 QB_NAME 即可。


HASH_JOIN_PROBE(t1_name,t2_name…)

使用说明
HASH_JOIN_PROBE(t1_name,t2_name ...),用于控制优化器对指定表使用 HASH JOIN 算法,同时将指定表作为 HASH JOIN 算法的 Probe 端,即用指定表作为 HASH JOIN 的探测(Probe)表。
参考示例
EXPLAIN SELECT /*+ HASH_JOIN_PROBE(t2)*/ * FROM t t1, t t2 WHERE t1.a = t2.a;
此条 SQL 指定了 t2 表作为 HASH JOIN 中的 Probe 表,最终计划如下图所示。

除了指定单个表外,还可以指定 JOIN 中间结果作为 Probe 端,示例如下。
EXPLAIN SELECT /*+ HASH_JOIN_PROBE((t1@sel_2,t3@sel_1)) */ * FROM (SELECT t1.a, t1.b FROM t t1, t t2 WHERE t1.a = t2.a) t1, t t3 WHERE t1.b = t3.b;
如上所示,通过将 t1 表与 t2 表用括号括起来,并指定好每个表的 QB_NAME 即可,最终计划如下图所示。


LEADING(t1_name,t2_name …)

使用说明
Leading(t1_name,t2_name...),用于控制优化器在 Join Reorder 阶段生成的 Join Order,优化器会按照 Leading Hint 中出现的顺序来确定 Join Order。
参考示例
EXPLAIN SELECT /*+ LEADING(t1,t3,t2,t4)*/ * FROM t1,t2,t3,t4 WHERE t1.a = t2.a and t2.a = t3.a and t3.a = t4.a;
此条 SQL 通过 LEADING Hint 显示指定了 t1 表首先与 t3 表进行 JOIN 操作,接着与 t2 表进行 JOIN,最终与 t4 表进行 JOIN,最终计划如下图所示。

上述方法指定 JOIN 顺序只能生成 Left Deep Tree,只读分析引擎还为 LEADING 提供了一种进阶语法,可以通过括号配合生成 Bushy Tree,示例如下。
EXPLAIN SELECT /*+ LEADING((t1,t3),(t2,t4))*/ * FROM t1,t2,t3,t4 WHERE t1.a = t2.a and t2.a = t3.a and t3.a = t4.a;
此条 SQL 的 LEADING Hint 首先通过将 (t1,t3) 控制优化器先进行 t1 表和 t3 表的 JOIN,之后将通过 (t2,t4) 控制优化器进行 t2 表与 t4 表的 JOIN,最后通过 ((t1,t3),(t2,t4)) 控制优化器将上述两个 JOIN 操作的结果进行 JOIN,最终执行计划如下图所示。

注意:
存在多个 LEADING Hint 时会导致 Hint 失效。
优化器无法按照 LEADING 进行表连接时 Hint 会失效。

SET_VAR(NAME="VALUE")

使用说明
SET_VAR(XXXX="YY"),用于在 SQL 执行期间临时修改系统变量,在 SQL 执行完毕后,指定系统变量会自动恢复为原始值,使用方法如下所示。
参考示例
SELECT /*+ SET_VAR(max_threads=64) */ * FROM t1
此条 SQL 通过 SET_VAR Hint 临时指定了在 SQL 执行期间的最大线程数为 64。
注意:
并不是所有参数都支持 SET_VAR Hint,使用前请确认目标参数可以支持 Hint 修改。支持通过 Hint 修改的 参数请参考 系统变量

NO_PX_JOIN_FILTER_ID(ID)/PX_JOIN_FILTER_ID(ID)

使用说明
no_px_join_filter_id(ID)/px_join_filter_id(ID),用于控制优化器关闭或开启 RuntimeFilter。
参考示例
具体使用方式参考 Runtime Filter 使用手册

常见 Hint 问题

MYSQL 客户端消除 Hint 导致不生效

MySQL 命令行客户端在 5.7 版本之前默认清除了 Optimizer Hints。如果需要在这些早期版本的客户端中使用 Hint 语法,需要在启动客户端时加上 --comments 选项,例如:mysql -h 127.0.0.1 -P 4000 -u root -c

跨库查询不指定库名导致 Hint 不生效

对于查询中有需要跨库访问的表,需要显示的在 Hint 中指定数据库名称,否则可能会出现 Hint 不生效的问题。例如对于下面这条 SQL:
SELECT /*+ SHUFFLE_JOIN(t1) */ * FROM test1.t1, test2.t2 WHERE t1.id = t2.id;
由于当前 t1 表在当前 database 中,从而导致 Hint 失效。Warning 信息如下所示。
mysql> show warnings;
+---------+------+---------------------------------------------------------------------------------------------------------------------------------------------------------+
| Level | Code | Message |
+---------+------+---------------------------------------------------------------------------------------------------------------------------------------------------------+
| Warning | 1815 | There are no matching table names for (t1) in optimizer hint /*+ SHUFFLE_JOIN(t1) */ or /*+ SHUFFLE_JOIN(t1) */. Maybe you can use the table alias name |
+---------+------+---------------------------------------------------------------------------------------------------------------------------------------------------------+
1 row in set (0.00 sec)

QB_NAME 未指定/指定错误,导致 Hint 不生效

对于有多个 QB_NAME 的查询,如果 Hint 没有写在目标表的 Query Block 中,则需要显示的在 Hint 中指定 QB_NAME,若没有指定,则可能发生 Query Hint 不生效的问题。例如:
SELECT /*+ HASH_JOIN_PROBE(t2) */ * FROM (SELECT t1.a, t1.b FROM t t1, t t2 WHERE t1.a = t2.a) t1, t t3 WHERE t1.b = t3.b;
由于 Hint 中的 t1 表没有显示指定 QB_NAME,导致 Hint 没有生效,Warning 信息如下。
mysql> show warnings;
+---------+------+---------------------------------------------------------------------------------------------------------------------------------+
| Level | Code | Message |
+---------+------+---------------------------------------------------------------------------------------------------------------------------------+
| Warning | 1815 | There are no matching table names for (t2) in optimizer hint /*+ HASH_JOIN_PROBE(t2) */. Maybe you can use the table alias name |
+---------+------+---------------------------------------------------------------------------------------------------------------------------------+
1 row in set (0.00 sec)
此时可通过关键字查询 SQL 计划,确认每个表所在 Query Block 的 QB_NAME。


Hint 位置不正确导致不生效

如果没有按照 Optimizer Hints 语法将 Hint 正确地放在指定关键字的后面,它将无法生效。例如:
MySQL> SELECT * /*+ SET_VAR(max_threads = 64)) */ FROM t;

ERROR 1064 (42000): You have an error in your SQL syntax; check the manual that corresponds to your MySQL server version for the right syntax to use line 1 column 42 near "/*+ SET_VAR(max_threads = 64)) */ FROM t"


帮助和支持

本页内容是否解决了您的问题?

填写满意度调查问卷,共创更好文档体验。

文档反馈