Por que o LIKE é mais que 4x mais rápido que o MATCH ... CONTRA em um índice FULLTEXT no MySQL?

12

Eu não estou entendendo isso.

Eu tenho uma tabela com esses índices

PRIMARY     post_id
INDEX       topic_id
FULLTEXT    post_text

A tabela possui (apenas) 346 000 linhas. Estou tentando realizar 2 consultas.

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')

leva 4,05 segundos enquanto

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%')

leva 0,027 segundos.

EXPLAIN mostra que a única diferença está em possible_keys ( fulltextinclui post_text, LIKEnão)

Isso é realmente estranho.

O que há por trás disso? O que está acontecendo em segundo plano? Como pode LIKEser tão rápido quando não está usando o índice e o FULLTEXT tão lento quando está usando o seu índice?

UPDATE1:

Na verdade, agora leva cerca de 0,5 segundos, talvez a tabela esteja bloqueada, mas ainda assim, quando ligo o perfil, é mostrado que a INICIALIZAÇÃO DO FULLTEXT levou 0,2 segundos. E aí?

Posso consultar minha tabela com LIKE10x por segundo, com texto completo apenas 2x

UPDATE2:

Surpresa!

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)

então eu estou perguntando, como isso é possível?

Além disso,

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

é muito lento. O texto completo pode estar quebrado?

UPDATE3:

Que diabos?

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

leva 0,27s enquanto

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

leva mais de 30 segundos! o que há de errado aqui?

gênese
fonte
Os tempos de resposta entre os dois são consistentes em várias execuções? Estou tentado a pensar que o cache de disco pode estar entrando em jogo, onde um primeiro teste "lento" carrega todos os dados necessários no RAM, de modo que a segunda consulta "rápida" é muito rápida.
Atxdba 18/03/12
Teste as consultas apenas com SQL_NO_CACHE .
mgutt
Esta é uma pergunta / resposta bastante antiga. Algum avanço do mysql / mariadb desde aqueles dias?
Roman Susi
11
Cuidado: O momento das perguntas e respostas implica que ele está falando apenas sobre MyISAM. Sua aplicabilidade ao InnoDB está em questão.
Rick James
@RomanSusi - Gostaria de iniciar uma nova pergunta voltada para o InnoDB?
Rick James

Respostas:

2

Eu acho que o problema pode resultar da presença do próprio índice FULLTEXT.

Sempre que há uma consulta envolvendo um índice FULLTEXT, o MySQL Query Optimizer tende a atacar a consulta em uma verificação completa da tabela. Eu tenho visto isso ao longo dos anos. Também escrevi um post anterior sobre esse comportamento mais insignificante nos índices do FULLTEXT .

Você pode precisar fazer duas coisas:

  1. refatorar a consulta para que o índice FULLTEXT não jogue o MySQL Query Optimizer em um estado de confusão
  2. Adicione um índice adicional que ofereça suporte adequado à consulta refatorada

REFator A Consulta

Aqui está sua consulta original

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') 

Você precisará refatorar a consulta assim:

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);

CRIAR UM NOVO ÍNDICE

Você precisará de um índice para dar suporte subqueryA. Você já tem um índice ativado topic_id. Você precisa substituí-lo da seguinte maneira:

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

De uma chance !!!

UPDATE 2012-03-19 13:08 EDT

Experimente este primeiro

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

Se isso for rápido e retornar um pequeno número de linhas, tente esta subconsulta aninhada:

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');

UPDATE 2012-03-19 13:11 EDT

Compare o tempo de execução disso:

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

com isso

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

Se o tempo de execução for o mesmo, a cláusula MATCH será executada em todas as linhas. Como mencionei anteriormente, o uso de índices FULLTEXT tende a anular quaisquer benefícios tentados e contribuídos pelo MySQL Query Optimizer.

RolandoMySQLDBA
fonte
Então, você quer dizer que minha consulta realmente verifica a tabela inteira porque topic_id e a post_idconfunde? Por que a consulta LIKE funciona mesmo sem ter índice nessas colunas (topic_id, post_id)? Por que o MYSQL não seleciona apenas inteligentemente topic_id = 144017 AND post_id != 155352e depois apenas navega por esses resultados? E se 100k linhas incluírem minha string de pesquisa de texto completo post_text? Não selecionaria todos eles?
genesis
Na verdade, estou confuso ainda mais. COMO '% text%' também não usa índices, significa que faz a varredura de toda a tabela, por que é tão rápido?
genesis
Por favor, olhe para a minha atualização , acho que você vai resolvê-lo muito rápido. Vou lhe dar meu representante se você resolver.
genesis
Respondendo à sua segunda atualização. A segunda consulta foi executada em menos de 0,01ms, a primeira não foi concluída. Por que você disse "Se o tempo de execução é o mesmo, a cláusula MATCH está sendo executada em todas as linhas". ? Não é exatamente o oposto do que deveria ser? Se você olhar aqui , verá que não sou o único com esse problema
genesis
Respondendo à sua primeira atualização. A primeira consulta foi executada em 0,01ms, 0 linhas, a segunda retornou "Não foi possível encontrar o índice FULLTEXT correspondente à lista de colunas". No entanto, sua consulta com 2 subconsultas funciona perfeitamente!
genesis