【问题标题】:MySQL performance with GROUP BY and JOIN使用 GROUP BY 和 JOIN 的 MySQL 性能
【发布时间】:2011-07-21 00:08:53
【问题描述】:

在花了很多时间处理这个问题的变体之后,我想知道是否有人可以帮助我优化这个查询或索引。

我有三个临时表 ref1、ref2、ref3,所有定义如下,ref1 和 ref2 各有大约 6000 行,而 ref3 只有 3 行:

CREATE TEMPORARY TABLE ref1 (
  id INT NOT NULL AUTO_INCREMENT,
  val INT,
  PRIMARY KEY (id)
)
ENGINE = MEMORY;

慢查询是针对这样的表,大约有 1M 行:

CREATE TABLE t1 (
  d DATETIME NOT NULL,
  id1 INT NOT NULL,
  id2 INT NOT NULL,
  id3 INT NOT NULL,
  x INT NULL,
  PRIMARY KEY (id1, d, id2, id3)
)
ENGINE = INNODB;

有问题的查询:

SELECT id1, SUM(x)
  FROM t1
  INNER JOIN ref1 ON ref1.id = t1.id1
  INNER JOIN ref2 ON ref2.id = t1.id2
  INNER JOIN ref3 ON ref3.id = t1.id3
  WHERE d BETWEEN '2011-03-01' AND '2011-04-01'
  GROUP BY id1;

临时表用于将结果集过滤为用户正在寻找的项目。

解释

+----+-------------+-------+--------+---------------+---------+---------+------------------+------+---------------------------------+
| id | select_type | table | type   | possible_keys | key     | key_len | ref              | rows | Extra                           |
+----+-------------+-------+--------+---------------+---------+---------+------------------+------+---------------------------------+
|  1 | SIMPLE      | ref1  | ALL    | PRIMARY       | NULL    | NULL    | NULL             | 6000 | Using temporary; Using filesort |
|  1 | SIMPLE      | t1    | ref    | PRIMARY       | PRIMARY | 4       | med31new.ref1.id |   38 | Using where                     |
|  1 | SIMPLE      | ref3  | ALL    | PRIMARY       | NULL    | NULL    | NULL             |    3 | Using where; Using join buffer  |
|  1 | SIMPLE      | ref2  | eq_ref | PRIMARY       | PRIMARY | 4       | med31new.t1.id2  |    1 |                                 |
+----+-------------+-------+--------+---------------+---------+---------+------------------+------+---------------------------------+

(在具有约 500 万行 EXPLAIN 的不同系统上,在列表中首先显示 t1,并带有“使用位置;使用索引;使用临时;使用文件排序”)

我是否遗漏了一些明显的东西会阻止使用临时表?

【问题讨论】:

  • 什么返回这个查询:SELECT count(*) FROM t1 WHERE d BETWEEN '2011-03-01' AND '2011-04-01'?

标签: mysql performance


【解决方案1】:

First filesort 并不意味着将文件写入磁盘来执行排序,它是 mySQL 中快速排序算法的名称,请查看what-does-using-filesort-mean-in-mysql

所以你的解释中有问题的关键字是Using temporary,而不是Using filesort。为此,您可以使用tmp_table_sizemax_heap_table_size(在两者上设置相同的值)以允许更多的内存工作并避免创建临时表,请查看this link on the subject with remarks about documentation mistakes

然后你可以尝试不同的索引策略,看看结果,但不要试图避免文件排序。

最后一件事,不相关,您创建一个SUM(x),但 x 可以采用 NULL 值,如果您不希望 Group 上的任何 NULL 值使您的总和为 NULL,SUM(COALESCE(x) , 0) 可能会更好。

【讨论】:

  • 你说得对,我真的想避免临时表。对于堆,我尝试了几个高达 1G 的值,每个都没有效果,这让我认为这是一个逻辑问题而不是资源问题。感谢 cmets。
【解决方案2】:

仅在 DATE 上添加索引。由于这是第一个表的标准,而其他表只是联接,它将首先针对 DATE 进行优化...联接是次要的。

【讨论】:

  • 日期索引无效(MySQL 忽略它)。我的理解是 GROUP BY id1 也需要一个索引,因此 (id1, d, ...) 上的 PK。
  • 将此标记为答案,因为在某些情况下它确实解决了问题,尽管我经常不得不使用优化器提示(使用索引...)。在其他情况下,需要在其中一个过滤表上使用 FORCE INDEX,通常当它只有几行并且 MySQL 想要对其进行表扫描时。如果我有一个限制,我发现我需要在 GROUP BY 中的列上强制索引,而不是使用 WHERE 中的列。我不喜欢使用优化器提示,但我发现它们在某些情况下非常重要。
【解决方案3】:

这不是吗:

SELECT id1, SUM(x)
  FROM t1
  INNER JOIN ref1 ON ref1.id = t1.id1
  INNER JOIN ref2 ON ref2.id = t1.id2
  INNER JOIN ref3 ON ref3.id = t1.id3
  WHERE d BETWEEN '2011-03-01' AND '2011-04-01'
  GROUP BY id1;

完全等同于:

select id1, SUM(x)
  FROM t1
  WHERE d BETWEEN '2011-03-01' AND '2011-04-01'
  group by id1;

额外的表格有什么用?我认为另一个答案中提到的临时表是指 MySQL 在查询执行期间创建临时表。如果您希望创建一个子查询(或表)以最小化连接中所需的操作数量,这可能会加快查询速度,但我没有看到选择连接数据。

【讨论】:

  • 不,这些不相等。 ref 表是过滤器,用于减少结果集。例如,如果 t1.id1 的值为 1-10,而 ref1.id 只有 1-3,则最终结果集将不包含 t1.id1 > 3 的任何行。这类似于说“WHERE t1.id1 IN (SELECT DISTINCT id FROM ref1)",只是更高效。
猜你喜欢
  • 1970-01-01
  • 2016-03-06
  • 1970-01-01
  • 2017-06-19
  • 1970-01-01
  • 1970-01-01
  • 2020-04-28
  • 2020-05-17
  • 1970-01-01
相关资源
最近更新 更多