Filesort查询,有索引,但没有使用它

时间:2012-11-14 15:11:12

标签: mysql

为什么以下查询无法使用story_keywords表中的索引story_id?

mysql> EXPLAIN SELECT `stories`.*
    -> FROM (`stories`)
    -> JOIN `story_keywords` ON `story_keywords`.`story_id` = `stories`.`id`
    -> WHERE `image_full_url` != ''
    -> AND `order` != 0
    -> AND `news_type` IN ('movie', 'movie_review') 
    -> AND `keyword` IN ('topnews', 'toptablet') 
    -> GROUP BY `stories`.`id`
    -> ORDER BY `created` DESC, `order` DESC
    -> LIMIT 5 ;
+----+-------------+----------------+--------+---------------+---------+---------+---------------------------------------+------+----------------------------------------------+
| id | select_type | table          | type   | possible_keys | key     | key_len | ref                                   | rows | Extra                                        |
+----+-------------+----------------+--------+---------------+---------+---------+---------------------------------------+------+----------------------------------------------+
|  1 | SIMPLE      | story_keywords | ALL    | story_id      | NULL    | NULL    | NULL                                  |   42 | Using where; Using temporary; Using filesort |
|  1 | SIMPLE      | stories        | eq_ref | PRIMARY       | PRIMARY | 767     | entertainment.story_keywords.story_id |    1 | Using where                                  |
+----+-------------+----------------+--------+---------------+---------+---------+---------------------------------------+------+----------------------------------------------+
2 rows in set (0.00 sec)


mysql> show create table stories;
+---------+----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+
| Table   | Create Table                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                       |
+---------+----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+
| stories | CREATE TABLE `stories` (
  `id` varchar(255) COLLATE utf8_unicode_ci NOT NULL,
  `news_type` varchar(255) COLLATE utf8_unicode_ci DEFAULT NULL,
  `title` varchar(255) COLLATE utf8_unicode_ci DEFAULT NULL,
  `created` datetime DEFAULT NULL,
  `author` varchar(255) COLLATE utf8_unicode_ci DEFAULT NULL,
  `author_title` varchar(255) COLLATE utf8_unicode_ci DEFAULT NULL,
  `image_caption` text COLLATE utf8_unicode_ci,
  `image_credit` varchar(255) COLLATE utf8_unicode_ci DEFAULT NULL,
  `image_full_url` varchar(255) COLLATE utf8_unicode_ci DEFAULT NULL,
  `body` text COLLATE utf8_unicode_ci,
  `summary` text COLLATE utf8_unicode_ci,
  `external_url` varchar(255) COLLATE utf8_unicode_ci DEFAULT NULL,
  `order` int(10) DEFAULT NULL,
  PRIMARY KEY (`id`)
) ENGINE=MyISAM DEFAULT CHARSET=utf8 COLLATE=utf8_unicode_ci |
+---------+----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+
1 row in set (0.00 sec)

mysql> show create table story_keywords;
+----------------+-----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+
| Table          | Create Table                                                                                                                                                                                                                                                                                                                      |
+----------------+-----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+
| story_keywords | CREATE TABLE `story_keywords` (
  `id` int(10) NOT NULL AUTO_INCREMENT,
  `story_id` varchar(255) COLLATE utf8_unicode_ci NOT NULL,
  `keyword` varchar(255) COLLATE utf8_unicode_ci NOT NULL,
  PRIMARY KEY (`id`),
  KEY `story_id` (`story_id`)
) ENGINE=MyISAM AUTO_INCREMENT=85 DEFAULT CHARSET=utf8 COLLATE=utf8_unicode_ci |
+----------------+-----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+
1 row in set (0.00 sec)

2 个答案:

答案 0 :(得分:1)

可能是因为MySQL认为从story_keywords表中获取所有行并加入它们而不是使用索引会更便宜。一开始听起来很奇怪,但是,你看,如果你必须在一个表上执行100次索引查找,而这个表只有大约100行 - 读取所有行的成本会更低。解释很简单:索引查找(对于BTREE索引)是O(ln N),而读取N行是O(N)。显然,O(N)< N * O(ln N)。

要证明这一点 - 尝试从故事中选择一行(并且一个是指一行,而不是对整个表进行排序并限制结果;),就像:

SELECT `stories`.*
FROM (`stories`)
JOIN `story_keywords` ON `story_keywords`.`story_id` = `stories`.`id`
WHERE `stories`.id = SOMETHING

此查询更有可能转向story_keywords上的索引。

希望这能回答你的问题:)

答案 1 :(得分:1)

安东走在正确的轨道上,但我相信问题还有更多。正如我对OP的评论所说,id列很可能是INT类型。如解释所示,stories上主键的长度为767.通常对于INT类型,长度将为低个位数,但由于列为VARCHAR ,长度非常长。

回到主要问题,因为stories.news_typestories.orderstory_keywords.story_keywords上没有索引,优化程序决定对story_keywords进行全面扫描,因为它将产生最小的初始结果集。如果其中一个列上有索引,则可能首先使用该索引。如果添加查询可以使用的索引,则无需执行全表扫描。