MySQL:优化对记录范围的搜索.

Jak*_*zok 2 mysql sql optimization full-text-search range

我刚刚在我的应用程序中看到了一个非常慢的查询.表'新闻'有超过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)
Run Code Online (Sandbox Code Playgroud)

出于某种原因,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)
Run Code Online (Sandbox Code Playgroud)

我的查询在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 |
+----+-------------+-------+----------+--------------------------------+---------+---------+------+------+-----------------------------+
Run Code Online (Sandbox Code Playgroud)

为什么使用全文密钥而不是新闻时间?

显示创建表新闻如下:

 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 |
Run Code Online (Sandbox Code Playgroud)

这是为什么?

Umb*_*lla 5

"为什么?"的答案 只是MySQL查询优化器并不完美.有时它会选择不太理想的指数.您可以通过告诉MySQL使用索引提示使用哪个索引来纠正此问题

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;
Run Code Online (Sandbox Code Playgroud)

从文档: "你也可以使用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;
Run Code Online (Sandbox Code Playgroud)