Mysql 索引使用

Mysql index use

我有 2 个 table 有共同的领域。在一个 table 上,公共字段有一个索引 而另一方面没有。 运行 查询如下:

SELECT * 
FROM table_with_index
LEFT JOIN table_without_index ON table_with_index.comcol = table_without_index.comcol
WHERE 1

查询的性能远低于 运行 相反的 :

SELECT * 
FROM table_without_index 
LEFT JOIN table_with_indexON table_without_index.comcol = table_with_index.comcol 
WHERE 1

任何人都可以向我解释为什么以及在这种情况下使用索引背后的逻辑?

要检查 MySQL 查询优化器发生了什么,请显示这两个查询的 EXPLAIN 计划。是这样的:

EXPLAIN 
SELECT * FROM table_with_index
LEFT JOIN table_without_index ON table_with_index.comcol =       table_without_index.comcol
WHERE 1

EXPLAIN
SELECT * 
FROM table_without_index 
LEFT JOIN table_with_indexON table_without_index.comcol = table_with_index.comcol 
WHERE 1

您可以在查询前添加 EXPLAIN 以了解 MySQL 将如何使用索引以及它加入 table 的顺序。

查看 EXPLAIN output format 的文档,了解如何解释结果。

因为LEFT JOIN的缘故,table的顺序不能改变。 MySQL 需要在最终结果集中包括从左侧 table 开始的所有行,无论它们是否在右侧 table.

中有匹配项

INNER JOINs 上,MySQL 通常交换 tables 并将行数较少的 table 放在第一位,因为这样它的行数较少分析。

我们来看这个查询(这是您的 table 名称较短的查询):

SELECT *
FROM a
  LEFT JOIN b ON a.col = b.col
WHERE 1

如何MySQL 运行查询:

  1. 获取tablea中符合查询条件的第一行。如果 WHERE 或连接子句中的条件仅使用 table a 的字段和常量值,则包含部分或全部这些字段的索引仅用于过滤行符合条件。

  2. 在 table a 的一行被 select 编辑后,它转到执行计划中的下一个 table (这是 table b 在我们的查询中)。它必须 select 所有符合 WHERE 条件和 JOIN 条件的行。更具体地说,从 table b 编辑的 select 行必须匹配条件 b.col = X,其中 X 是列 col 的值对于当前从步骤 1 table a 编辑的行 select。它找到第一个匹配行,然后转到下一个 table。由于此查询中没有 "next table",它会将一对行(来自 ab)放入结果集中,然后丢弃来自 b 的行并搜索对于下一个,重复此步骤,直到找到 b 中与当前 select 从 a 编辑的行匹配的所有行(在步骤 1 中)。

  3. 如果在第 2 步中找不到 b 中与当前 select 从 a 编辑的行相匹配的任何行,则 LEFT JOIN 强制 MySQL 组成一行(具有 b 的列),其中包含 NULLs 并与来自 a 的当前行一起创建一行并将其放入结果集中.

  4. 处理完来自 b 的所有匹配行后,MySQL 丢弃来自 a 的当前行,select 丢弃来自 select 的下一行a 匹配 WHERE 和连接条件,并从 b 中匹配行的 selection 重新开始(步骤 2)。

  5. 此过程循环进行,直到处理完 a 中的所有行。

备注:

  • 步骤 1 中 "first row" 的含义取决于很多因素。例如,如果 table a 上有一个索引包含查询中指定的所有列(属于 table a),则 MySQL 将不会读取table 数据,但将改用索引。在这种情况下,行的顺序由索引给出。在其他情况下,行是从 table 数据中读取的,顺序由它们存储在存储介质上的顺序提供。

    这个简单的查询没有任何 WHERE 条件(WHERE 1 总是 TRUE)并且 JOIN 子句中没有条件只包含来自 a。 table a 中的所有行都包含在结果集中,如果可能的话,这会导致 full table scan 或索引扫描。

  • 在第 2 步中,如果 table b 在列 col 上有索引,则 MySQL 使用该索引从 b 在列 col 上具有值 X。这是一个快速的操作。如果 table b 在列 col 上没有索引,则 MySQL 需要执行 table bfull table scan。这意味着它必须读取 table b 的所有行 才能找到那些在列 col 上具有值 X 的行。这是一个非常缓慢且耗费资源的操作。

  • 因为tablea的行没有条件,MySQL不能用tablea的索引来过滤它 selects 的行。另一方面,当它需要 select 来自 table b 的行时(在第 2 步),它有一个匹配条件 (b.col = X) 并且它可以使用加速 selection 的索引,假设这样的索引存在于 table b.

这解释了您的两个查询之间的巨大性能差异。此外,由于 LEFT JOIN,您的两个查询不等价,它们产生不同的结果。

免责声明: 请注意,上面的步骤列表是对查询执行方式的过度简化说明。它试图用简单的语言来表达,并跳过幕后发生的许多技术方面。

有关如何使查询 运行 更快的提示可以在 MySQL 文档的 8. Optimization

部分找到