【问题标题】:MySQL query indexMySQL查询索引
【发布时间】:2021-12-13 19:26:03
【问题描述】:

我正在使用 MySQL 5.6 并尝试优化下一个查询:

SELECT t1.field1,
       ...
       t1.field30,
       t2.field1
FROM Table1 t1
         JOIN Table2 t2 ON t1.fk_int = t2.pk_int
WHERE t1.int_field = ?
  AND t1.enum_filed != 'value'
ORDER BY t1.created_datetime desc;

一个响应可以包含数百万条记录,每行包含 31 列。

现在 EXPLAIN 在 Extra 中说规划器使用“使用位置”。

我尝试添加下一个索引:

create index test_idx ON Table1 (int_field, enum_filed, created_datetime, fk_int);

之后 EXPLAIN 在 Extra 中说计划器使用“使用索引条件;使用文件排序”

带有索引的 EXPLAIN 中的“rows”值小于没有它的值。但实际上执行时间更长。

所以,接下来的问题是:

  1. 此查询的最佳索引是什么?
  2. 为什么 EXPLAIN 说带有索引的查询的 'key_len' 是 5。不应该是 4+1+8+4=17 吗?
  3. ORDER BY 中的字段是否应该在索引中?
  4. JOIN 中的字段是否应该在索引中?

【问题讨论】:

  • 什么是这个查询的最佳索引? 测试(int_field, enum_filed, fk_int, created_datetime) 和(int_field, fk_int, enum_filed, created_datetime)。 为什么 EXPLAIN 说带有索引的查询的 'key_len' 是 5。不应该是 4+1+8+4=17 吗? 只使用索引表达式中的前 2 列。 现在 EXPLAIN 在 Extra 中说计划器使用“使用位置” Filesort 也存在,但未提及。
  • MySQL 5.6 于 9 年前首次发布,到 2021 年 2 月结束生命周期,物有所值。如果您要投入大量工作来优化您的应用,最好使用当前版本的软件。
  • enum_filed 有多少个不同的值? (如果您没有注意到,向您提出的几个问题可以通过提供SHOW CREATE TABLE 来解决)
  • 当INT 显示为“5”时,提示可能它应该是NOT NULL。
  • @RickJames enum_filed 有 5 个不同的值。

标签: mysql sql indexing query-optimization


【解决方案1】:

尝试以这种方式重构您的索引
避免(o 在 fk_int 之后向右移动)created_datetime 列.. 并将 fk_int 移动到 enum_filed 列之前.. 这样用于过滤器的 3 个列应该更好地使用)

create index test_idx ON Table1 (int_field, fk_int, enum_filed);

确保您在 table2 列 pk_int 上也有特定索引。如果你还没有添加

create index test_idx ON Table2 (int_field, fk_int, enum_filed);

【讨论】:

    【解决方案2】:

    这个查询的最佳索引是什么?

    • 可能是(int_field, created_datetime)(原因请参见下一个问答。)

    为什么 EXPLAIN 说带索引的查询的 'key_len' 是 5。不应该是 4+1+8+4=17 吗?

    • enum_filed != 击败优化器。如果该枚举只有一个其他值(它是NOT NULL),则使用= 和其他值。并尝试INDEX(int_field, enum_field, created_datetime) 优化器对= 比对任何不等式更满意。
    • “5”可以表示 2 列,也可以表示一个可空的INT。如果int_field可以是NULL,考虑改成NOT NULL;那么“5”会下降到“4”。

    ORDER BY 中的字段应该在索引中吗?

    • 只有索引可以完全处理WHERE。这通常仅在所有WHERE 测试都是= 时才会发生。 (因此,我之前的回答。)
    • 包含这些列的另一种情况是“覆盖”;请参阅下一个问答。

    JOIN 中的字段应该在索引中吗?

    • 视情况而定。提供一些性能优势的一件事是在SELECT 中包含任何地方提到的所有列。这称为“覆盖”索引,在EXPLAIN 中由Using index(不是Using index condition)表示。 t1 中的列太多,无法添加“覆盖”索引。我认为实际的限制是大约 5 列。

    【讨论】:

      【解决方案3】:

      我对你的问题 № 1 的猜测:

      create index my_idx on Table1(int_field, created_datetime desc, fk_int)
      

      或其中之一(但可能都不值得):

      create index my_idx on Table1(int_field, created_datetime desc, enum_filed, fk_int)
      
      create index my_idx on Table1(int_field, created_datetime desc, fk_int, enum_filed)
      

      我假设有 3 件事:

      • Table2.pk_int 已经是主键了,从名字上看
      • Table1.int_field 上的where 条件仅满足Table1 的一小部分
      • Table1.enum_filed 上的不等式(如果我是你,我会改正错字)只排除了Table1 的一小部分

      问题№2:key_len 指的是使用的键。不要忘记可空键有一个额外的字节。在您的情况下,如果 int_field 可以为空,则表示这是唯一使用的密钥,否则使用 int_field 和 enum_filed。

      至于问题 3 和 4:如果像我想的那样,从组合索引 Table1.int_field 上的 where 条件开始查询计划效率更高,在这种情况下也使用正确的排序顺序(@ 987654336@),允许扫描索引以正确顺序获取输出行,而无需额外的排序步骤。此外,将fk_int 也添加到索引使得检索Table1 的任何记录变得不必要除非在Table2 中存在相应的记录。出于类似的原因,您也可以将enum_filed 添加到索引中,但是,如果这不会显着减少输出记录数,则索引大小的增加会使事情变得更糟而不是更好。最后,您必须尝试一下(使用真实数据!)。

      请注意,如果您在索引中的int_field 和created_datetime 之间放置另一列,则索引将不会以所需的输出顺序提供created_datetime(对于给定的int_field)。

      【讨论】:

        【解决方案4】:

        通过向查询添加更多过滤器(where 子句)解决了该问题。 关于索引 2 建议的索引很有帮助:

        1. 来自@WalterTross,下一个索引用于初始查询:

        (int_field, created_datetime desc, enum_filed, fk_int)

        我的简短评论:MySQL 5.6 不支持 desc 索引 - 这个关键字只是保留。

        1. 来自@RickJames,下一个索引用于修改查询:

        (int_field, created_datetime)

        感谢所有试图提供帮助的人。我真的很感激。

        【讨论】:

          猜你喜欢
          • 2012-02-01
          • 1970-01-01
          • 2012-06-11
          • 2011-04-10
          • 1970-01-01
          • 1970-01-01
          • 2016-02-16
          • 2014-03-17
          • 2013-07-10
          相关资源
          最近更新 更多