简单的SELECT mysql查询非常慢(使用交叉)

时间:2013-04-18 06:31:57

标签: mysql

过去在生产服务器上运行良好的查询已经开始变得非常缓慢(在几个小时内)。

就是这样:

SELECT * FROM news_articles WHERE published = '1' AND news_category_id = '4' ORDER BY date_edited DESC LIMIT 1;

这需要 20-30秒才能执行(该表有~200.000行)

这是EXPLAIN

的输出
+----+-------------+---------------+-------------+----------------------------+----------------------------+---------+------+------+--------------------------------------------------------------------------+
| id | select_type | table         | type        | possible_keys              | key                        | key_len | ref  | rows | Extra                                                                    |
+----+-------------+---------------+-------------+----------------------------+----------------------------+---------+------+------+--------------------------------------------------------------------------+
|  1 | SIMPLE      | news_articles | index_merge | news_category_id,published | news_category_id,published | 5,5     | NULL | 8409 | Using intersect(news_category_id,published); Using where; Using filesort |
+----+-------------+---------------+-------------+----------------------------+----------------------------+---------+------+------+--------------------------------------------------------------------------+

玩弄它,我发现暗示一个特定的索引(date_edited)会让它更快:

SELECT * FROM news_articles USE INDEX (date_edited) WHERE published = '1' AND news_category_id = '4' ORDER BY date_edited DESC LIMIT 1;

这个毫秒执行

这个的

EXPLAIN输出是:

+----+-------------+---------------+-------+---------------+-------------+---------+------+------+-------------+
| id | select_type | table         | type  | possible_keys | key         | key_len | ref  | rows | Extra       |
+----+-------------+---------------+-------+---------------+-------------+---------+------+------+-------------+
|  1 | SIMPLE      | news_articles | index | NULL          | date_edited | 8       | NULL |    1 | Using where |
+----+-------------+---------------+-------+---------------+-------------+---------+------+------+-------------+

news_category_idpublisheddate_edited都已编入索引。

存储引擎是InnoDB。

这是表结构:

CREATE TABLE `news_articles` (
  `id` int(11) NOT NULL AUTO_INCREMENT,
  `title` text NOT NULL,
  `subtitle` text NOT NULL,
  `summary` text NOT NULL,
  `keywords` varchar(500) DEFAULT NULL,
  `body` mediumtext NOT NULL,
  `source` varchar(255) DEFAULT NULL,
  `source_visible` int(11) DEFAULT NULL,
  `author_information` enum('none','name','signature') NOT NULL     DEFAULT 'name',
  `date_added` datetime NOT NULL,
  `date_edited` datetime NOT NULL,
  `views` int(11) DEFAULT '0',
  `news_category_id` int(11) DEFAULT NULL,
  `user_id` int(11) DEFAULT NULL,
  `c_forwarded` int(11) DEFAULT '0',
  `published` int(11) DEFAULT '0',
  `deleted` int(11) DEFAULT '0',
  `permalink` varchar(255) DEFAULT NULL,
  PRIMARY KEY (`id`),
  KEY `user_id` (`user_id`),
  KEY `news_category_id` (`news_category_id`),
  KEY `published` (`published`),
  KEY `deleted` (`deleted`),
  KEY `date_edited` (`date_edited`),
  CONSTRAINT `news_articles_ibfk_3` FOREIGN KEY (`news_category_id`) REFERENCES `news_categories` (`id`) ON DELETE SET NULL ON UPDATE CASCADE,
  CONSTRAINT `news_articles_ibfk_4` FOREIGN KEY (`user_id`) REFERENCES `users` (`id`) ON DELETE SET NULL ON UPDATE CASCADE
) ENGINE=InnoDB AUTO_INCREMENT=192588 DEFAULT CHARSET=utf8

我可能会更改我的Web应用程序使用该索引提示的所有查询。但这是相当大的工作。

是否有某种方法可以调整MySQL,以便在不重写所有查询的情况下提高第一个查询的效率?

3 个答案:

答案 0 :(得分:2)

只是一些提示..

1 - 在我看来,发布的字段和news_category_id是INTEGER。如果是,请从查询中删除单引号。它可以在性能方面产生巨大的差异;

2 - 另外,我会说你发布的字段没有很多不同的值(可能是1 - 是和0 - 否,或类似的东西)。如果我是对的,这根本不是一个很好的索引领域。在这种情况下,解析仍然需要通过所有记录来查找它正在寻找的内容;在这种情况下,将news_category_id移动到WHERE子句中的第一个字段。

3 - “不要忘记最左边的索引”。此肯定对SELECT,JOINS,WHERE,ORDER BY有效。即使桌子上的柱子的位置也很重要,保持索引的位置在顶部。索引是你的朋友,只要你知道如何玩它们。

希望它能以某种方式帮助你......

SELECT * FROM news_articles WHERE published ='1'AND news_category_id ='4'ORDER BY date_edited DESC LIMIT 1;

答案 1 :(得分:0)

原件: SELECT * FROM news_articles WHERE published = 1 AND news_category_id = 4 ORDER BY date_edited DESC LIMIT 1;

由于您有 LIMIT 1 ,因此您只选择最新一行。 ORDER BY date_edited 告诉MySQL排序然后从顶部取1行。这真的很慢,为什么 USE INDEX 会有所帮助。

尝试匹配 WHERE子句中的 MAX(date_edited)。这应该让查询规划器自动使用它的索引。

选择MAX(date_entered): SELECT * FROM news_articles WHERE published = 1 AND news_category_id = 4 AND date_edited = (select max(date_edited) from news_articles);

答案 2 :(得分:-1)

请将您的查询更改为:

SELECT * FROM news_articles WHERE published = 1 AND news_category_id = 4 ORDER BY date_edited DESC LIMIT 1;

请注意,我已从查询

中提供的“1”和“4”数据中删除了引号

传递的数据类型和列结构的差异不允许mysql能够在这两列上使用索引。