为什么在MySQL的FULLTEXT索引上,LIKE比MATCH…AGAINST快4倍以上?


12

我不明白这一点。

我有一张带有这些索引的表

PRIMARY     post_id
INDEX       topic_id
FULLTEXT    post_text

表具有(仅)346 000行。我正在尝试执行2个查询。

SELECT post_id 
FROM phpbb_posts 
WHERE topic_id = 144017 
AND post_id != 155352 
AND MATCH(post_text) AGAINST('http://rapidshare.com/files/5494794/photo.rar')

需要4.05秒,而

SELECT post_id 
FROM phpbb_posts 
WHERE topic_id=144017 
AND post_id != 155352 
AND post_text LIKE ('%http://rapidshare.com/files/5494794/photo.rar%')

需要0.027秒。

EXPLAIN显示唯一的区别是possible_keys(fulltext包括post_text,LIKE没有)

真的很奇怪

这背后是什么?后台发生了什么事?LIKE不使用索引时怎么能这么快,而使用FULLTEXT时却那么慢呢?

UPDATE1:

实际上,现在大约需要0.5秒,也许表已锁定,但是,当我打开分析时,它表明FULLTEXT INITIALIZATION花费了0.2秒。这是怎么回事?

LIKE每秒查询表10次​​,全文查询2次

UPDATE2:

惊喜!

mysql> SELECT post_id FROM phpbb_posts WHERE post_id != 2 AND topic_id = 6 AND MATCH(post_text) AGAINST ('rapidshare.com');
Empty set (0.04 sec)

所以我问,这怎么可能?

另外,

SELECT count(*) FROM phpbb_posts WHERE MATCH(post_text) AGAINST ('rapidshare.com')

真的很慢 全文可能有破损吗?

UPDATE3:

我勒个去?

SELECT forum_id, post_id, topic_id, post_text  FROM phpbb_posts  WHERE MATCH(post_text) AGAINST ('rapidshare.com') LIMIT 0, 30;

需要0.27秒,而

SELECT count(*) FROM phpbb_posts  WHERE MATCH(post_text) AGAINST ('rapidshare.com') LIMIT 0, 30;

需要超过30秒!这是怎么了?


两次运行之间的响应时间是否一致?我很容易想到磁盘缓存可能正在发挥作用,其中第一个“慢速”测试将所有需要的数据加载到ram中,因此第二个“快速”查询速度很快。
atxdba 2012年

仅使用SQL_NO_CACHE测试查询。
mgutt 2015年

这是很老的问题/答案。从那以后,mysql / mariadb有什么进步?
罗曼·苏西

1
警告:此次问答的时间表明它仅是在谈论MyISAM。它对InnoDB的适用性尚存疑问。
瑞克·詹姆斯

@RomanSusi-您想针对InnoDB提出一个新问题吗?
瑞克·詹姆斯

Answers:


2

我认为问题可能源于FULLTEXT索引本身的存在。

每当有一个涉及FULLTEXT索引的查询时,MySQL Query Optimizer就会将查询重整为全表扫描。多年来,我已经看到了这一点。我还写了一篇有关FULLTEXT索引中这种最琐碎行为的早期文章

您可能需要做两件事:

  1. 重构查询,以使FULLTEXT索引不会使MySQL Query Optimizer陷入混乱状态
  2. 添加一个附加索引,该索引将正确支持重构查询

重新查询查询

这是您的原始查询

SELECT post_id  
FROM phpbb_posts  
WHERE topic_id = 144017  
AND post_id != 155352  
AND MATCH(post_text) AGAINST('http://rapidshare.com/files/5494794/photo.rar') 

您将需要像这样重构查询:

SELECT subqueryA.post_id
FROM
(
    SELECT post_id FROM phpbb_posts
    WHERE topic_id = 144017
    AND post_id != 155352
) subqueryA
INNER JOIN
(
    SELECT post_id FROM phpbb_posts
    WHERE MATCH(post_text) AGAINST('http://rapidshare.com/files/5494794/photo.rar')
) subqueryB
USING (post_id);

创建新索引

您将需要一个索引来支持subqueryA。您已经有一个索引topic_id。您需要按以下方式替换它:

ALTER TABLE phpbb_posts ADD INDEX topic_post_ndx (topic_id,post_id);
ALTER TABLE phpbb_posts DROP INDEX topic_id;

试试看 !!!

更新2012-03-19 13:08 EDT

首先尝试这个

SELECT post_id FROM
(
    SELECT * FROM phpbb_posts
    WHERE topic_id = 144017
    AND post_id != 155352
) A;

如果运行速度很快并返回少量行,请尝试以下嵌套子查询:

SELECT post_id FROM
(
    SELECT * FROM phpbb_posts
    WHERE topic_id = 144017
    AND post_id != 155352
) A
WHERE MATCH(post_text) AGAINST('http://rapidshare.com/files/5494794/photo.rar');

更新2012-03-19 13:11 EDT

比较一下它的运行时间:

SELECT count(*) FROM phpbb_posts  WHERE MATCH(post_text) AGAINST ('rapidshare.com') LIMIT 0, 30;

有了这个

SELECT count(*) FROM phpbb_posts WHERE 1 = 1;

如果运行时间相同,则MATCH子句将在每一行上执行。正如我之前提到的,使用FULLTEXT索引往往会使MySQL Query Optimizer尝试和贡献的任何好处都无效。


因此,您想说我的查询实际上扫描了整个表,因为topic_id并使之post_id混淆了吗?为什么即使在这些列(topic_id,post_id)上都没有索引,LIKE查询也能工作?为什么MYSQL不仅仅智能地选择topic_id = 144017 AND post_id != 155352然后仅浏览这些结果?如果100k行中包含我的全文本搜索字符串post_text怎么办?它不会全部选中吗?
起源

其实我更加困惑。像'%text%'也不使用索引,这意味着它可以扫描整个表,那么为什么这么快?
起源

请查看我的UPDATE,我认为您会很快解决它。如果您解决了问题,我将给您我的代表。
起源

回复您的第二次更新。第二个查询的运行时间少于0.01ms,第一个查询未完成。为什么说“如果运行时间相同,则MATCH子句将在每一行上执行。” ?这不是应该的相反吗?如果您在这里查看,您会发现我不是唯一遇到此问题的人
起源

回复您的第一次更新。第一个查询运行0.01ms,0行,第二个查询返回“找不到与列列表匹配的FULLTEXT索引”。但是,带有2个子查询的查询效果很好!
起源
By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.