查找此MySQL查询的最佳索引

时间:2016-06-07 16:57:54

标签: mysql sql optimization query-optimization

在MySQL慢查询日志中,我有以下查询:

SELECT * FROM `news_items`
WHERE `ctime` > 1465013901 AND `feed_id` IN (1, 2, 9) AND
`moderated` = '1' AND `visibility` = '1'
ORDER BY `views` DESC
LIMIT 5;

以下是EXPLAIN的结果:

+----+-------------+------------+-------+---------------------------------------------------------------------------------------+-------+---------+------+------+-------------+
| id | select_type | table      | type  | possible_keys                                                                         | key   | key_len | ref  | rows | Extra       |
+----+-------------+------------+-------+---------------------------------------------------------------------------------------+-------+---------+------+------+-------------+
|  1 | SIMPLE      | news_items | index | feed_id,ctime,ctime_2,feed_id_2,moderated,visibility,feed_id_3,cday_complex,feed_id_4 | views | 4       | NULL |    5 | Using where |
+----+-------------+------------+-------+---------------------------------------------------------------------------------------+-------+---------+------+------+-------------+
1 row in set (0.00 sec)

当我手动运行此查询时,它需要0.00秒但由于某种原因它出现在MySQL的慢速日志中,有时需要1-5秒。我相信当服务器处于高负载时会发生这种情况。

这是表结构:

CREATE TABLE IF NOT EXISTS `news_items` (
  `item_id` int(10) NOT NULL,
  `category_id` int(10) NOT NULL,
  `source_id` int(10) NOT NULL,
  `feed_id` int(10) NOT NULL,
  `title` varchar(255) CHARACTER SET utf8 NOT NULL,
  `announce` varchar(255) CHARACTER SET utf8 NOT NULL,
  `content` text CHARACTER SET utf8 NOT NULL,
  `hyperlink` varchar(255) CHARACTER SET utf8 NOT NULL,
  `ctime` varchar(11) CHARACTER SET utf8 NOT NULL,
  `cday` tinyint(2) NOT NULL,
  `img` varchar(100) CHARACTER SET utf8 NOT NULL,
  `video` text CHARACTER SET utf8 NOT NULL,
  `gallery` text CHARACTER SET utf8 NOT NULL,
  `comments` int(11) NOT NULL DEFAULT '0',
  `views` int(11) NOT NULL DEFAULT '0',
  `visibility` enum('1','0') CHARACTER SET utf8 NOT NULL DEFAULT '0',
  `pin` tinyint(1) NOT NULL,
  `pin_dttm` varchar(10) CHARACTER SET utf8 NOT NULL,
  `moderated` tinyint(1) NOT NULL
) ENGINE=MyISAM DEFAULT CHARSET=utf8;

名为“views”的索引仅包含1个字段 - 视图。 我还有许多其他索引由(例如):

组成
feed_id + views + visibility + moderated
moderated + visibility + feed_id + ctime
moderated + visibility + feed_id + views + ctime

我使用了上述顺序的字段,因为这是MySQL开始使用它们的唯一原因。但是,我从来没有在EXPLAIN中使用“在哪里使用索引”。

有关如何让EXPLAIN向我显示“使用索引”的任何想法?

3 个答案:

答案 0 :(得分:0)

如果您已将存储引擎更改为InnoDB并创建正确的复合索引,则可以尝试此操作。第一个查询只获取前5行的item_id。完成SELECT后完成限制。因此,没有任何大数据就可以做到这一点,然后只从5个问题中获得孔行

SELECT idata.* FROM (
  SELECT item_id FROM `news_items`
  WHERE `ctime` > 1465013901 AND `feed_id` IN (1, 2, 9) AND
  `moderated` = '1' AND `visibility` = '1'
  ORDER BY `views` DESC
  LIMIT 5 ) as i_ids
LEFT JOIN news_items AS idata ON idata.item_id = i_ids.item_id
ORDER BY `views` DESC;

答案 1 :(得分:0)

如果你的表"还有许多其他索引",为什么它们不会显示在SHOW CREATE TABLE

有两种方式

WHERE `ctime` > 1465013901
  AND `feed_id` IN (1, 2, 9)
  AND `moderated` = '1'
  AND `visibility` = '1'
ORDER BY `views` DESC

可以使用索引:

  • INDEX(views)并希望提前显示所需的5行(请参阅LIMIT)。
  • INDEX(moderated, visibility, feed_id, ctime)

这最后一个复合材料' index以比较= constant的两列(按任意顺序)开始,然后转到IN,最后转到"范围" (ctime > const)。旧版本不会超过IN;较新版本将跳过IN值并使用ctime上的范围。 More discussion

ORDER BY列的所有之前,将WHERE列包含在复合索引中是没用的。但是,在您的案例中包含views是没有用的,因为"范围"在ctime

关于懒惰评估的提示' Bernd建议也会有所帮助。

我同意InnoDB可能会更好。 Conversion tips

答案 2 :(得分:-1)

回答你的问题:"使用索引"意味着MySQL将使用 only 索引来满足您的查询。要做到这一点,我们需要创建一个"覆盖" index(索引"涵盖"查询)=索引,涵盖"其中"和"按顺序排列"来自"选择"的所有字段但是,你正在做"选择*"所以这是不切实际的。

MySQL在视图中选择索引,因为查询中有限制5。这样做是因为1)索引很小2)在这种情况下它可以避免使用filesort。

我认为问题不在于索引,而在于引擎= MyISAM。 MyISAM使用表级锁定,因此如果更改news_items,它将被锁定。我建议将表转换为InnoDB。

另一种可能性是,如果表很大,索引(视图)可能不是最佳选择。

如果您使用Percona Server,则可以启用慢速日志详细程度选项,并查看慢速查询的查询计划,如下所述:https://www.percona.com/doc/percona-server/5.5/diagnostics/slow_extended_55.html