有什么办法可以强制 MySQL 使用 Hash Join 而不是 Nested Loop Join?

zli*_*i89 7 mysql join

根据文档MySQL Explain Output format,MySQL 使用嵌套循环连接方法解析所有连接。有没有办法强制 MySQL 使用散列连接?

ype*_*eᵀᴹ 5

直到(最近发布的)5.6 版本,不,你不能。不幸的是,优化器知道的唯一方法是嵌套循环方法。正如5.6 文档所说:

MySQL 使用嵌套循环连接方法解析所有连接。

换句话说,即使在5.7 版本中也没有实现哈希连接算法或变体- 尽管它仍在开发中(希望永远不会消亡)。


有一种选择。MariaDB是 MySQL 的一个分支,它在最新的(5.3 和 5.5)版本中实现了各种其他连接方法,可以直接替换 MySQL(分别为 5.1 和 5.5)版本。

在他们的Block-Based Join Algorithms页面中,他们描述了新方法:

在 5.3 之前的 MariaDB/MySQL 版本中,只实现了一种基于块的连接算法:块嵌套循环 (BNL) 连接算法。它只能用于内部联接。MariaDB 5.3(及更高版本)增强了 BNL 连接的实现,并提供了多种基于块的连接算法,可用于内连接、外连接和半连接。MariaDB 中基于块的连接算法使用连接缓冲区来累积第一个连接操作数的记录,然后再开始在第二个连接操作数中查找匹配项。

本页记录了各种基于块的连接算法。

  • 块嵌套循环 (BNL) 连接
  • 块嵌套循环哈希 (BNLH) 连接
  • 块索引连接称为批量密钥访问 (BKA) 连接
  • 块索引哈希联接称为批量密钥访问哈希 (BKAH) 联接

还有一些优化器开关会影响优化查询时将考虑的算法。


Rol*_*DBA 3

很不幸的是,不行。为什么 ?两个原因:

原因#1:评估策略

根据设计,查询是使用以下算法循环评估的:

  • 确定哪些键可用于从表中检索记录,并为每个表选择最佳的键。
  • 对于每个表,确定表扫描是否比读取键更好。如果有很多记录与键值匹配,则键的优势就会降低,表扫描会变得更快。
  • 确定当查询中存在多个表时应连接表的顺序。
  • 重写 WHERE 子句以消除死代码,减少不必要的计算并尽可能更改约束,为使用键开辟道路。
  • 从连接中消除未使用的表。
  • 确定键是否可用于ORDER BYGROUP BY
  • 尝试简化子查询,并确定可以缓存其结果的程度。
  • 合并视图(将视图引用扩展为宏)

我在我的Mar 11, 2013帖子中提供了更多详细信息:JOIN 条件和 WHERE 条件之间存在执行差异吗?

原因#2:使用子查询优化查询

查询优化器在嵌套连接方法之上有自己的秘密武器。这涉及在任何转换阶段删除行。这缩短了收集结果集的时间。但是,在使用子查询进行查询时,这可能不会产生正确的DELETE结果UPDATE

我有关于此的帖子:

更新 2015-10-14 11:09 美国东部时间

查看我的答案,然后查看@ypercube的答案,我开始意识到@ypercube实际上暗示了更改优化器开关设置。到目前为止还没人明确提及这一点,所以我会的。

最近,我为内部公司客户端配置了以下内容/etc/my.cnf

[mysqld]
optimizer_switch = block_nested_loop=off
Run Code Online (Sandbox Code Playgroud)

然后,为了跳过重新启动 mysql,我运行了这个

mysql> SET GLOBAL optimizer_switch = block_nested_loop=off;
Run Code Online (Sandbox Code Playgroud)

客户端麻烦的连接查询现在运行得更好了。

请阅读有关 optimizer_switch 的 MySQL 文档

至于实际问题,只有MySQL查询优化器才能决定最佳的连接方法。但是,您可以通过禁用来提供帮助block_nested_loop