【问题标题】:Most efficient indexes to use for a query using WHERE and GROUP BY?用于使用 WHERE 和 GROUP BY 的查询的最有效索引?
【发布时间】:2012-02-18 06:46:38
【问题描述】:

我有一个大约 700 万行的表,我不断地运行这种查询:

SELECT 
      MyField, 
      COUNT(*) 
   FROM 
      MyTable 
   WHERE 
          MyField2='ConstantValue' 
      AND MyField NOT IN ( SELECT Field 
                              FROM AnotherTable) 
      AND Timestamp >= [ArbitraryTimestamp] 
   GROUP BY 
      MyField;

上述字段的基数:

  • MyField = 大约 40,000 个不同的值。
  • 时间戳 = 大多数是不同的,因此大约有 700 万个不同的值。
  • MyField2 = 2 个不同的值。
  • 来自 AnotherTable 的字段 = 大约 50 个不同的值。

正如预期的那样,这运行得非常慢,使用EXPLAIN 告诉我我是Using where; Using temporary; Using filesort

我想通过向该表添加索引来提高这些查询的效率,但我不确定最好的方法是什么。

我应该在MyField 上添加索引吗?并索引Timestamp?两个都?两者的组合索引?

另外,我还能做些什么来加快这类查询的速度吗?

【问题讨论】:

  • 如果查询不完整,我们无法为您提供完整的答案,请发布 WHERE 子句的“...”。
  • 其他琐碎的选择标准非常重要。索引的使用取决于 WHERE 中的所有内容。
  • 好的,已更新问题。

标签: mysql sql optimization indexing query-optimization


【解决方案1】:

你应该先用 MyField 添加一个双键索引(分组依据):

CREATE INDEX MyIndex
    ON MyTable (MyField, Timestamp)

【讨论】:

  • 老实说,我认为向时间戳列添加索引不会有太大帮助。它具有高基数,这将导致较大的索引占用空间。
  • @MikePurcell,这取决于表格和用例。如果表很大,有 50 个其他列,并且他需要经常进行此组计数,那么值得使用磁盘空间对其进行索引。
【解决方案2】:

MyField 上的 GROUPBY 将强制 MySQL 创建一个临时表,这就是为什么您在 EXPLAIN 中获得 using temporary 的原因。并且创建一个大约 700 万行的临时表肯定会是一个痛苦的查询。

尝试几件事(在实施每个建议后,重新运行查询并检查查询时间):

  1. 为 MyField 添加索引
  2. 查看是否可以通过添加最大时间戳(结合您的最小时间戳)来限制行数
  3. 专门为 MyField2 和 MyField3 列添加索引
  4. 如果您的查询响应仍然很慢,请尝试在所有三列(Myfield、MyField2 和 MyField3)中添加复合索引

如果上述方法都不能立即帮助您,请查看此 post 描述如何使用子查询来获取计数,这完全避免了 GROUP BY。

【讨论】:

  • 1.好像完全没有影响。 2.不太可能,最大时间戳通常是当前时间,因此不会真正缩小行数。 3. 这样做会使执行时间加倍。 4. 我搞混了,MyField 和 MyField3 在这种情况下是相同的,而 MyField2 在这种情况下的基数是 2,所以这并没有什么帮助。我认为长时间执行的罪魁祸首实际上是MyField3 NOT IN (SELECT Field FROM AnotherTable),因为删除它会大大提高性能。
  • 事实证明,当我问这个问题时,我的脑海中混杂了几个不同的查询。我已将其更新为更简洁。
  • 看起来 DRApp 的建议是正确的。我什至没有考虑加入。下次包括整个查询,以便您获得更好的帮助。
  • 抱歉,我试图使用GROUP BY 将其推广到一整组查询,但后来它变成了一个关于这个需要很长时间的特定查询的问题。下次我会尽量说清楚。
  • 不用担心。最终结果是你的问题得到了回答,所以赢了!
【解决方案3】:

除了 Mike 和 DKamins 的建议之外,子查询可能是另一个瓶颈。我会将其重组为 LEFT JOIN 并根据 IS NULL 应用(确保“AnotherTable”在连接的“字段”上有索引

SELECT 
      MyField, 
      COUNT(*) 
   FROM 
      MyTable 
         LEFT join AnotherTable
            on MyTable.MyField = AnotherTable.Field
   WHERE 
          MyField2='ConstantValue' 
      AND AnotherTable.Field IS NULL 
      AND Timestamp >= [ArbitraryTimestamp] 
   GROUP BY 
      MyField;

我会专门根据 (MyField2, TimeStamp, MyField) 建立我的索引。 MyField2 是您的特定限定符,不要费心在您的重点中包含其他杂项...从那里,您正在查看特定的时间范围...从那里,拥有 MyField 也将有助于优化 GroupBy。键/位置的前两部分专门用于过滤您的条件,因此其余部分已按正确顺序进行分组。

【讨论】:

  • 谢谢!这种方式大大减少了查询时间。
  • @DanieL,很高兴听到......顺便说一句,多么重要......你没有确定原始查询有多“慢”。了解性能改进可以帮助其他人在一定程度上了解某些处理方式的影响以及影响因素。
  • 原始查询耗时 2 分 20 秒,而使用您的建议对其进行修改将其缩短为 28 秒。
猜你喜欢
  • 2020-10-29
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2020-12-27
  • 2021-08-17
  • 1970-01-01
  • 2018-11-08
  • 1970-01-01
相关资源
最近更新 更多