暂无图片
暂无图片
暂无图片
暂无图片
暂无图片

MySQL 8.0 | 新特性哈希连接(Hash Join)

数据库实用技能 2021-04-19
2205


        2019 年 10 月 14 日 正式发布了 MySQL 8.0.18 GA 版本,带来了一些新特性和增强功能,其中最引人注目的是多表连接查询支持 hash join,有了Hash Join,SQL的性能将得到显著的提升.

       MySQL Hash Join的特性介绍:

       1、对于大数据量的表关联,hash join速度将明显比Block Nested Loop快很多

       2、在内存中处理

       3、必要情况下,会使用磁盘空间

4、用于内连接,可扩展到外连接、半连接和反连接

5、替换查询计划中的Block Nested Loop

6、可以通过HINT强制SQL走hash join或者Block Nested Loop

       从MySQL 8.0.18开始,MySQL对任何查询都具有相等连接条件且不使用索引的查询使用哈希连接,例如:

SELECT *

 FROM t1

 JOIN t2

   ON t1.c1=t2.c1;

       大多数情况下,hash join 比之前的 Block Nested-Loop 算法在没有索引时的等值连接更加高效。使用以下语句创建三张测试表:

CREATE TABLE t1 (c1 INT, c2 INT);

CREATE TABLE t2 (c1 INT, c2 INT);

CREATE TABLE t3 (c1 INT, c2 INT);

使用EXPLAIN FORMAT=TREE命令可以看到执行计划中的 hash join,例如:

mysql> EXPLAIN FORMAT=TREE

         SELECT *

       FROM t1

       JOIN t2

       ON t1.c1=t2.c1\G

*************************** 1. row ***************************

EXPLAIN: -> 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)

       要查看是否将散列连接用于给定的连接,必须EXPLAIN与 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 FORMAT=TREE命令的输出进行查看:

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)

         从以上输出同样可以看出,包含多个等值连接条件的查询也可以(会)使用多个 hash join 连接。

          但是,如果任何连接语句(ON)中没有使用等值连接条件,将不会采用 hash join 连接方式。例如:

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:

       这时,将会采用性能更慢的 block nested loop 连接算法。这与 MySQL 8.0.18 之前版本中没有索引时的情况一样:

mysql> EXPLAIN

 ->   SELECT *

 ->     FROM t1

 ->     JOIN t2

 ->       ON (t1.c1 = t2.c1)

 ->     JOIN t3

 ->       ON (t2.c1 < t3.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 (Block Nested Loop)

*************************** 3. row ***************************

     id: 1

select_type: SIMPLE

   table: t3

 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 (Block Nested Loop)

        Hash join 连接同样适用于不指定查询条件时的笛卡尔积(Cartesian product),例如:

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尽可能使用哈希联接。仅在MySQL 8.0.18中,可以通过以下两种方式之一控制使用哈希联接:

  • 在全局或会话级别上,通过使用 hash_join=on或 hash_join=off作为optimizer_switch服务器系统变量设置的一部分。默认值为hash_join=on。

  • 通过使用优化器提示之一HASH_JOIN 或 NO_HASH_JOIN作为给定联接的一部分,逐个案例地进行。

      (从MySQL 8.0.19开始,hash_join 优化器开关以及 HASH_JOIN和 NO_HASH_JOIN优化器提示不再起作用。)

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

  • 增加join_buffer_size以使哈希联接不会溢出到磁盘。

  • 增加open_files_limit。

       从MySQL 8.0.18开始,使用批处理密钥访问(BKA)的哈希连接的连接缓冲区是递增分配的;因此,对于使用增量分配的内部join_buffer_size联接BKA,您可以设置较高的值而无需使用少量查询来分配非常大的RAM,但是外部联接会分配整个缓冲区。在MySQL 8.0.20及更高版本中,哈希联接也用于外部联接(包括反联接和半联接),因此这不再是问题。

         


文章转载自数据库实用技能,如果涉嫌侵权,请发送邮件至:contact@modb.pro进行举报,并提供相关证据,一经查实,墨天轮将立刻删除相关内容。

评论