我刚刚在我的应用程序中看到一个非常慢的查询。表“新闻”有超过 600.000 条记录。
当我执行时:
SELECT news.id FROM `news` WHERE (newstime between '2012-01-16 00:00:00' AND '2012-01-16 23:59:59') AND ((MATCH(titolo, testo) AGAINST('"Public Administration" "SOMETHING" "ELSE" "ROMA" "MILANO"' IN BOOLEAN MODE))) ORDER BY newstime DESC LIMIT 23 OFFSET 0;
23 rows in set (26.32 sec)
由于某种原因,MySQL 没有执行范围选择(每天只有 10.000 条记录),看起来它正在整个表上搜索,因为当我从子查询中选择时:
SELECT id FROM(SELECT * from news where newstime between '2012-01-16 00:00:00' AND '2012-01-16 23:59:59') as N where ((MATCH(titolo, testo) AGAINST('"Public Administration" "FIAT" "SOMETHING" "ELSE" "ROMA" "MILANO"' IN BOOLEAN MODE))) ORDER BY newstime DESC LIMIT 23 OFFSET 0;
23 rows in set (0.09 sec)
我的查询在 0.09 秒后返回!
26 -> 0.09 秒。
我以为MySQL会足够智能,可以按新闻时间选择记录范围,然后进行全文搜索,但看起来并非如此。正常吗?或者我应该尝试仍然优化第一个查询?当我写解释 #1 时,它的意思是:
mysql> explain SELECT news.id FROM `news` WHERE (newstime between '2012-01-16 00:00:00' AND '2012-01-16 23:59:59') AND ((MATCH(titolo, testo) AGAINST('"Public Administration" "FIAT" "SOMETHING" "ELSE" "ROMA" "MILANO"' IN BOOLEAN MODE))) ORDER BY newstime DESC LIMIT 23 OFFSET 0;
+----+-------------+-------+----------+--------------------------------+---------+---------+------+------+-----------------------------+
| id | select_type | table | type | possible_keys | key | key_len | ref | rows | Extra |
+----+-------------+-------+----------+--------------------------------+---------+---------+------+------+-----------------------------+
| 1 | SIMPLE | news | fulltext | index_news_on_newstime,alltext | alltext | 0 | | 1 | Using where; Using filesort |
+----+-------------+-------+----------+--------------------------------+---------+---------+------+------+-----------------------------+
为什么使用全文键而不是新闻时间?
显示建表消息如下:
CREATE TABLE `news` (
`id` int(10) unsigned NOT NULL AUTO_INCREMENT,
`titolo` varchar(255) DEFAULT NULL,
`testo` mediumtext,
`newstime` datetime NOT NULL,
`created_at` datetime DEFAULT NULL
PRIMARY KEY (`id`),
KEY `index_news_on_newstime` (`newstime`),
FULLTEXT KEY `alltext` (`titolo`,`testo`)
) ENGINE=MyISAM AUTO_INCREMENT=1846714 DEFAULT CHARSET=utf8 |
这是为什么呢?
最佳答案
“为什么?”的答案只是MySQL查询优化器并不完美。有时它会选择不太理想的索引。您可以通过告诉 MySQL 使用 index hints 哪个索引来纠正此问题。
SELECT news.id
FROM `news`
USE INDEX (index_news_on_newstime)
WHERE (newstime between '2012-01-16 00:00:00' AND '2012-01-16 23:59:59')
AND ((MATCH(titolo, testo) AGAINST('"Public Administration" "SOMETHING" "ELSE" "ROMA" "MILANO"' IN BOOLEAN MODE)))
ORDER BY newstime DESC LIMIT 23 OFFSET 0;
来自文档: “您还可以使用 FORCE INDEX,其作用类似于 USE INDEX (index_list),但表扫描被认为是非常昂贵的。换句话说,只有在无法进行表扫描的情况下才使用表扫描。使用给定索引之一查找表中的行。”
SELECT news.id
FROM `news`
FORCE INDEX (index_news_on_newstime)
WHERE (newstime between '2012-01-16 00:00:00' AND '2012-01-16 23:59:59')
AND ((MATCH(titolo, testo) AGAINST('"Public Administration" "SOMETHING" "ELSE" "ROMA" "MILANO"' IN BOOLEAN MODE)))
ORDER BY newstime DESC LIMIT 23 OFFSET 0;
关于MySQL:优化对记录范围的搜索。,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/8881331/