【问题标题】:Optimise MySql query using temporary and filesort使用临时和文件排序优化 MySql 查询
【发布时间】:2011-10-27 12:26:37
【问题描述】:

我有这个查询(如下所示),它当前使用临时和文件排序来生成按一组有序结果分组的结果。如果可能的话,我想摆脱他们的使用。我查看了此查询中使用的基础索引,但看不到缺少什么。

SELECT 
  b.institutionid AS b__institutionid,
  b.name AS b__name,  
  COUNT(DISTINCT f2.facebook_id) AS f2__0 
FROM education_institutions b 
LEFT JOIN facebook_education_matches f ON b.institutionid = f.institutionid 
LEFT JOIN facebook_education f2 ON f.school_uid = f2.school_uid 
WHERE 
  (
  b.approved = '1' 
  AND f2.facebook_id IN ( [lots of facebook ids here ])
  ) 
GROUP BY b__institutionid 
ORDER BY f2__0 DESC
LIMIT 10

这是EXPLAIN EXTENDED 的输出:

+----+-------------+-------+--------+--------------------------------+----------------+---------+----------------------------------+------+----------+----------------------------------------------+
| id | select_type | table | type   | possible_keys                  | key            | key_len | ref                              | rows | filtered | Extra                                        |
+----+-------------+-------+--------+--------------------------------+----------------+---------+----------------------------------+------+----------+----------------------------------------------+
|  1 | SIMPLE      | f     | index  | PRIMARY,institutionId          | institutionId  | 4       | NULL                             |  308 |   100.00 | Using index; Using temporary; Using filesort |
|  1 | SIMPLE      | f2    | ref    | facebook_id_idx,school_uid_idx | school_uid_idx | 9       | f.school_uid                     |    1 |   100.00 | Using where                                  |
|  1 | SIMPLE      | b     | eq_ref | PRIMARY                        | PRIMARY        | 4       | f.institutionId                  |    1 |   100.00 | Using where                                  |
+----+-------------+-------+--------+--------------------------------+----------------+---------+----------------------------------+------+----------+----------------------------------------------+

每个表的CREATE TABLE 语句如下所示,以便您了解架构。

CREATE TABLE facebook_education (
  education_id int(11) NOT NULL AUTO_INCREMENT,
  name varchar(255) DEFAULT NULL,
  school_uid bigint(20) DEFAULT NULL,
  school_type varchar(255) DEFAULT NULL,
  year smallint(6) DEFAULT NULL,
  facebook_id bigint(20) DEFAULT NULL,
  degree varchar(255) DEFAULT NULL,
  PRIMARY KEY (education_id),
  KEY facebook_id_idx (facebook_id),
  KEY school_uid_idx (school_uid),
  CONSTRAINT facebook_education_facebook_id_facebook_user_facebook_id FOREIGN KEY (facebook_id) REFERENCES facebook_user (facebook_id)
) ENGINE=InnoDB AUTO_INCREMENT=484 DEFAULT CHARSET=utf8;

CREATE TABLE facebook_education_matches (
  school_uid bigint(20) NOT NULL,
  institutionId int(11) NOT NULL,
  created_at timestamp NULL DEFAULT NULL,
  updated_at timestamp NULL DEFAULT NULL ON UPDATE CURRENT_TIMESTAMP,
  PRIMARY KEY (school_uid),
  KEY institutionId (institutionId),
  CONSTRAINT fk_facebook_education FOREIGN KEY (school_uid) REFERENCES facebook_education (school_uid) ON DELETE CASCADE ON UPDATE CASCADE,
  CONSTRAINT fk_education_institutions FOREIGN KEY (institutionId) REFERENCES education_institutions (institutionId) ON DELETE CASCADE ON UPDATE CASCADE
) ENGINE=InnoDB DEFAULT;

CREATE TABLE education_institutions (
  institutionId int(11) NOT NULL AUTO_INCREMENT,
  name varchar(100) NOT NULL,
  type enum('School','Degree') DEFAULT NULL,
  approved tinyint(1) NOT NULL DEFAULT '0',
  deleted tinyint(1) NOT NULL DEFAULT '0',
  normalisedName varchar(100) NOT NULL,
  created_at timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP,
  PRIMARY KEY (institutionId)
) ENGINE=InnoDB AUTO_INCREMENT=101327 DEFAULT CHARSET=utf8;

任何指导将不胜感激。

【问题讨论】:

    标签: mysql query-optimization


    【解决方案1】:

    文件排序可能是因为您没有合适的 ORDER BY 索引

    在 MySQL "ORDER BY Optimization" 文档中提到。

    您可以做的是加载一个临时表,然后从中选择。加载临时表时,使用ORDER BY NULL。当你从临时表中选择时,使用ORDER BY .. LIMIT

    问题在于 group by 添加了一个隐式 order by <group by clause> ASC ,除非您通过添加 order by null 来禁用该行为。
    这是 MySQL 特有的问题之一。

    【讨论】:

    • 当我删除 GROUP BY 子句时,'using filesort' 消失了!
    • @GordyD:这就是我给出这个答案的原因:-)
    • 将 ORDER BY 子句更改为带有索引的内容对使用 filesort 没有任何影响。只有当我完全删除 GROUP BY 时,才会使用文件排序。我完全不明白为什么您的回答与此有关。
    • GROUP BY 强制排序,假设 ORDER BY 相同 (b__institutionid)。 ORDER BY NULL 告诉优化器不要这样做。在我的链接中有解释
    • 好吧,我现在明白了,谢谢你的澄清。不幸的是,尽管我认为创建临时表不是一个可行的解决方案。这个查询是由 Doctrine ORM 从 DQL 查询生成的,所以我希望我可以通过添加所需的索引来解决这个问题。按我正在使用的 COUNT 字段排序时,是否有另一种方法可以避免文件排序。
    【解决方案2】:

    我可以看到两种可能的优化,

    1. b.approved = '1' - 您肯定需要在已批准列上建立索引以进行快速过滤。

    2. f2.facebook_id IN ([这里有很多 facebook ids ])) - 将 facebook ids 存储在临时表中。然后在临时表上创建一个索引,然后加入临时表而不是使用IN子句。

    【讨论】:

    • 1.b.approved = '1' - You definitely need an index on approved column for quick filtering. 不行,因为approved是一个布尔字段并且基数很低,MySQL将简单地拒绝使用索引。
    猜你喜欢
    • 1970-01-01
    • 2014-02-13
    • 1970-01-01
    • 2012-04-17
    • 1970-01-01
    • 2010-11-08
    • 2014-01-04
    • 2017-09-09
    • 1970-01-01
    相关资源
    最近更新 更多