过去在生产服务器上运行良好的查询已经开始变得非常缓慢(在几个小时内)。
就是这样:
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_id
,published
和date_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,以便在不重写所有查询的情况下提高第一个查询的效率?
答案 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能够在这两列上使用索引。