8.2.1.4 哈希连接优化

您所在的位置:网站首页 连接哈希 8.2.1.4 哈希连接优化

8.2.1.4 哈希连接优化

#8.2.1.4 哈希连接优化| 来源: 网络整理| 查看: 265

8.2.1.4 哈希连接优化

默认情况下,MySQL(8.0.18 及更高版本)尽可能使用散列连接。BNL可以使用和 NO_BNL优化器提示之一,或者通过设置 block_nested_loop=on或 block_nested_loop=off作为 optimizer_switch 服务器系统变量设置的一部分 来控制是否使用散列连接 。

笔记

MySQL 8.0.18 支持在 中设置 hash_join标志 optimizer_switch,以及优化器提示 HASH_JOIN和 NO_HASH_JOIN. 在 MySQL 8.0.19 及更高版本中,这些都不再有效。

从 MySQL 8.0.18 开始,MySQL 对每个连接都有等值连接条件的任何查询采用哈希连接,并且其中没有可应用于任何连接条件的索引,例如:

SELECT * FROM t1 JOIN t2 ON t1.c1=t2.c1;

当有一个或多个索引可用于单表谓词时,也可以使用散列连接。

散列连接通常比以前版本的 MySQL 中使用的块嵌套循环算法(请参阅块嵌套循环连接算法) 更快,并且旨在用于这种情况 。从 MySQL 8.0.20 开始,删除了对块嵌套循环的支持,并且服务器在以前使用块嵌套循环的任何地方使用散列连接。

在刚刚显示的示例和本节中的其余示例中,我们假设三个表 t1、t2和 t3是使用以下语句创建的:

CREATE TABLE t1 (c1 INT, c2 INT); CREATE TABLE t2 (c1 INT, c2 INT); CREATE TABLE t3 (c1 INT, c2 INT);

您可以看到正在通过 using 使用散列连接 EXPLAIN,如下所示:

mysql> EXPLAIN -> SELECT * FROM t1 -> JOIN t2 ON t1.c1=t2.c1\G *************************** 1. row *************************** id: 1 select_type: SIMPLE table: t1 partitions: NULL type: ALL possible_keys: NULL key: NULL key_len: NULL ref: NULL rows: 1 filtered: 100.00 Extra: NULL *************************** 2. row *************************** id: 1 select_type: SIMPLE table: t2 partitions: NULL type: ALL possible_keys: NULL key: NULL key_len: NULL ref: NULL rows: 1 filtered: 100.00 Extra: Using where; Using join buffer (hash join)

(在 MySQL 8.0.20 之前,有必要包含 FORMAT=TREE选项以查看散列连接是否被用于给定的连接。)

EXPLAIN ANALYZE还显示有关使用的散列连接的信息。

散列连接也用于涉及多个连接的查询,只要每对表的至少一个连接条件是等值连接,如下所示的查询:

SELECT * FROM t1 JOIN t2 ON (t1.c1 = t2.c1 AND t1.c2 < t2.c2) JOIN t3 ON (t2.c1 = t3.c1);

在刚刚显示的使用内部联接的情况下,在执行联接后,任何不是等值联接的额外条件都将作为过滤器应用。(对于外部连接,例如左连接、半连接和反连接,它们作为连接的一部分打印出来。)这可以在 的输出中看到EXPLAIN:

mysql> EXPLAIN FORMAT=TREE -> SELECT * -> FROM t1 -> JOIN t2 -> ON (t1.c1 = t2.c1 AND t1.c2 < t2.c2) -> JOIN t3 -> ON (t2.c1 = t3.c1)\G *************************** 1. row *************************** EXPLAIN: -> Inner hash join (t3.c1 = t1.c1) (cost=1.05 rows=1) -> Table scan on t3 (cost=0.35 rows=1) -> Hash -> Filter: (t1.c2 < t2.c2) (cost=0.70 rows=1) -> Inner hash join (t2.c1 = t1.c1) (cost=0.70 rows=1) -> Table scan on t2 (cost=0.35 rows=1) -> Hash -> Table scan on t1 (cost=0.35 rows=1)

从刚刚显示的输出中也可以看出,多个散列连接可以(并且已经)用于具有多个等值连接条件的连接。

在 MySQL 8.0.20 之前,如果任何一对连接表不具备至少一个 equi-join 条件,则无法使用哈希连接,并且采用较慢的块嵌套循环算法。在 MySQL 8.0.20 及更高版本中,哈希连接用于这种情况,如下所示:

mysql> EXPLAIN FORMAT=TREE -> SELECT * FROM t1 -> JOIN t2 ON (t1.c1 = t2.c1) -> JOIN t3 ON (t2.c1 < t3.c1)\G *************************** 1. row *************************** EXPLAIN: -> Filter: (t1.c1 < t3.c1) (cost=1.05 rows=1) -> Inner hash join (no condition) (cost=1.05 rows=1) -> Table scan on t3 (cost=0.35 rows=1) -> Hash -> Inner hash join (t2.c1 = t1.c1) (cost=0.70 rows=1) -> Table scan on t2 (cost=0.35 rows=1) -> Hash -> Table scan on t1 (cost=0.35 rows=1)

(本节后面提供了其他示例。)

散列连接也适用于笛卡尔积——也就是说,当没有指定连接条件时,如下所示:

mysql> EXPLAIN FORMAT=TREE -> SELECT * -> FROM t1 -> JOIN t2 -> WHERE t1.c2 > 50\G *************************** 1. row *************************** EXPLAIN: -> Inner hash join (cost=0.70 rows=1) -> Table scan on t2 (cost=0.35 rows=1) -> Hash -> Filter: (t1.c2 > 50) (cost=0.35 rows=1) -> Table scan on t1 (cost=0.35 rows=1)

在 MySQL 8.0.20 及更高版本中,连接不再需要包含至少一个等值连接条件才能使用散列连接。这意味着可以使用散列连接优化的查询类型包括以下列表中的那些(带示例):

内部非等连接:

mysql> EXPLAIN FORMAT=TREE SELECT * FROM t1 JOIN t2 ON t1.c1 < t2.c1\G *************************** 1. row *************************** EXPLAIN: -> Filter: (t1.c1 < t2.c1) (cost=4.70 rows=12) -> Inner hash join (no condition) (cost=4.70 rows=12) -> Table scan on t2 (cost=0.08 rows=6) -> Hash -> Table scan on t1 (cost=0.85 rows=6)

半连接:

mysql> EXPLAIN FORMAT=TREE SELECT * FROM t1 -> WHERE t1.c1 IN (SELECT t2.c2 FROM t2)\G *************************** 1. row *************************** EXPLAIN: -> Hash semijoin (t2.c2 = t1.c1) (cost=0.70 rows=1) -> Table scan on t1 (cost=0.35 rows=1) -> Hash -> Table scan on t2 (cost=0.35 rows=1)

反加入:

mysql> EXPLAIN FORMAT=TREE SELECT * FROM t2 -> WHERE NOT EXISTS (SELECT * FROM t1 WHERE t1.c1 = t2.c1)\G *************************** 1. row *************************** EXPLAIN: -> Hash antijoin (t1.c1 = t2.c1) (cost=0.70 rows=1) -> Table scan on t2 (cost=0.35 rows=1) -> Hash -> Table scan on t1 (cost=0.35 rows=1) 1 row in set, 1 warning (0.00 sec) mysql> SHOW WARNINGS\G *************************** 1. row *************************** Level: Note Code: 1276 Message: Field or reference 't3.t2.c1' of SELECT #2 was resolved in SELECT #1

左外连接:

mysql> EXPLAIN FORMAT=TREE SELECT * FROM t1 LEFT JOIN t2 ON t1.c1 = t2.c1\G *************************** 1. row *************************** EXPLAIN: -> Left hash join (t2.c1 = t1.c1) (cost=0.70 rows=1) -> Table scan on t1 (cost=0.35 rows=1) -> Hash -> Table scan on t2 (cost=0.35 rows=1)

右外连接(观察 MySQL 将所有右外连接重写为左外连接):

mysql> EXPLAIN FORMAT=TREE SELECT * FROM t1 RIGHT JOIN t2 ON t1.c1 = t2.c1\G *************************** 1. row *************************** EXPLAIN: -> Left hash join (t1.c1 = t2.c1) (cost=0.70 rows=1) -> Table scan on t2 (cost=0.35 rows=1) -> Hash -> Table scan on t1 (cost=0.35 rows=1)

默认情况下,MySQL 8.0.18 及更高版本会尽可能使用散列连接。BNL可以使用和 NO_BNL优化器提示 之一来控制是否使用散列连接 。

(MySQL 8.0.18 支持 hash_join=on或 hash_join=off作为 optimizer_switch服务器系统变量设置的一部分以及优化器提示 HASH_JOIN或 NO_HASH_JOIN。在 MySQL 8.0.19 及更高版本中,这些不再有任何效果。)

可以使用 join_buffer_size系统变量控制散列连接的内存使用;散列连接不能使用超过此数量的内存。当哈希连接所需的内存超过可用量时,MySQL 通过使用磁盘上的文件来处理。如果发生这种情况,您应该知道,如果哈希连接无法放入内存并且它创建的文件多于为 .set 设置的文件,则连接可能不会成功 open_files_limit。为避免此类问题,请进行以下任一更改:

增加join_buffer_size以使散列连接不会溢出到磁盘。

增加open_files_limit。

从 MySQL 8.0.18 开始,哈希连接的连接缓冲区是递增分配的;因此,您可以设置 join_buffer_size得更高,而无需分配大量 RAM 的小查询,但外部连接会分配整个缓冲区。在 MySQL 8.0.20 及更高版本中,哈希连接也用于外连接(包括反连接和半连接),因此这不再是问题。



【本文地址】


今日新闻


推荐新闻


CopyRight 2018-2019 办公设备维修网 版权所有 豫ICP备15022753号-3