MySQL使用filesort进行慢速查询

时间:2013-09-11 10:17:14

标签: mysql performance join explain

我有MySQL查询的速度问题。表定义如下:

CREATE TABLE IF NOT EXISTS `student` (
  `student_id` int(11) unsigned NOT NULL AUTO_INCREMENT,
  `forename` varchar(30) NOT NULL,
  `updated_time` timestamp NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP,
  `surname` varchar(50) NOT NULL,
  `student_college` int(11) DEFAULT NULL,
  `countup` smallint(5) unsigned DEFAULT NULL, 
  PRIMARY KEY (`student_id`),
  KEY `countup` (`countup`),
  KEY `student_sort` (`countup`,`updated_time`),
  KEY `student_college` (`student_college`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8;

CREATE TABLE IF NOT EXISTS `college` (
  `college_id` int(11) NOT NULL AUTO_INCREMENT,
  `college_name` varchar(100) NOT NULL DEFAULT 'Centre Name',
  `college_location` int(11) DEFAULT NULL,
  PRIMARY KEY (`college_id`),
  KEY `college_location` (`college_location`),
  KEY `college_name` (`college_name`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8;

查询如下:

SELECT *
FROM student
JOIN college ON student.student_college = college.college_id
WHERE  
college_location = 1
ORDER BY student.countup desc, student.updated_time desc
LIMIT 15;

我得到以下解释:

id select_type table   type possible_keys              key              key_len    ref                           rows Extra
1  SIMPLE      college ref  "PRIMARY,college_location" college_location 5          const                         915  Using where; Using temporary; Using filesort
1  SIMPLE      student ref  student_college            student_college  5          speed_test.college.college_id 50   Using where

学生表有大约500,000条记录,大学表有915行。第三个表用于保存学院的所有位置。我的查询需要检索特定位置的所有学生,然后按countup和updated_time对结果进行排序。我有一个关于countup和updated_time的综合索引。我想摆脱文件,但我找不到一个令人满意的方法。

我考虑过将college_location移到学生表中,以便将它组合成一个复合索引。有更好的解决方案吗?

3 个答案:

答案 0 :(得分:4)

下面的查询将删除使用临时;使用filesort。从解释,所以这应该在理论上更好..

MySQL优化器是愚蠢的,所以诀窍是强制优化器想要你想要的,这是一个基于college.college_location = 1的派生表。 因此,您可以使用学生表INNER JOIN结果。 这样MySQL可以使用排序键

SELECT 
 *
FROM 
 student
INNER JOIN (
    SELECT 
     college_id
    FROM 
     college
    WHERE
     college.college_location = 1  
  ) college
ON student.student_college = college.college_id
ORDER BY
    student.countup DESC
  , student.updated_time DESC

请注意大写锁定中的新索引

请参阅演示http://sqlfiddle.com/#!2/05c8a/1

或者,如果您认为它更有意义或更容易阅读,您可以使用它。 性能应该是相同的,因为解释向我解释它是相同的。

SELECT 
 * 
FROM (
  SELECT 
    college_id
  FROM 
    college
  WHERE
    college.college_location = 1  
) 
  college

INNER JOIN
 student 

ON
 student.student_college = college.college_id

ORDER BY
    student.countup DESC
  , student.updated_time DESC

参见演示http://sqlfiddle.com/#!2/05c8a/23

新策略分治方法 向数据库发出更多查询,以便使用正确的索引。 并且不需要临时表和filesort。

SET @college_ids = NULL; 

SELECT
  GROUP_CONCAT(college_id)
FROM
  college
WHERE
  college_location = 1
GROUP BY
  college_location ASC
INTO @college_ids;

SELECT 
 *
FROM 
 student
WHERE 
 student.student_college IN(@college_ids)
ORDER BY
    student.countup DESC
  , student.updated_time DESC
;

参见演示http://sqlfiddle.com/#!2/454b3/61

答案 1 :(得分:0)

我无法轻松测试,但请尝试使用此student_sort键:

KEY {student_sort {1}} {student_college {1}} {计数进位{1}} {updated_time {1}}

答案 2 :(得分:0)

尝试索引:

KEY student_sort(countup DESC ,updated_time DESC)

然后使用STRAIGHT_JOIN和FORCE INDEX:

SELECT *
FROM student force index(student_sort) STRAIGHT_JOIN 
     college 
         ON student.student_college = college.college_id
WHERE college_location = 1
ORDER BY student.countup desc, 
         student.updated_time desc
LIMIT 15;