Pourquoi LIKE est plus de 4x plus rapide que MATCH… AGAINST sur un index FULLTEXT dans MySQL?

12

Je ne comprends pas ça.

J'ai une table avec ces index

PRIMARY     post_id
INDEX       topic_id
FULLTEXT    post_text

La table a (seulement) 346 000 lignes. J'essaie d'effectuer 2 requêtes.

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

prend 4,05 secondes tandis que

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

prend 0,027 seconde.

EXPLAIN montre que la seule différence est dans possible_keys ( fulltexta post_text inclus, LIKEne le fait pas)

C'est vraiment étrange.

Qu'est-ce qui se cache derrière ça? Que se passe-t-il en arrière-plan? Comment peut-on LIKEêtre si rapide quand on n'utilise pas index et FULLTEXT si lent quand on utilise son index?

MISE À JOUR1:

En fait, cela prend maintenant environ 0,5 seconde, peut-être que la table était verrouillée, mais quand même, lorsque j'allume le profilage, cela montre que FULLTEXT INITIALIZATION a pris 0,2 seconde. Quoi de neuf?

Je peux interroger ma table avec LIKE10x par seconde, avec fulltext seulement 2x

MISE À JOUR2:

Surprise!

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)

donc je demande, comment est-ce possible?

Aditionellement,

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

est vraiment lent. Peut être en texte intégral tout cassé?

MISE À JOUR3:

Que se passe-t-il?

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

prend 0,27 s tout

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

prend plus de 30 secondes! Qu'est-ce qui ne va pas ici?

genèse
la source
Les temps de réponse entre les deux sont-ils cohérents sur plusieurs exécutions? Je suis tenté de penser que la mise en cache du disque pourrait entrer en jeu lorsqu'un premier test "lent" charge toutes les données nécessaires dans la mémoire RAM, de sorte que la deuxième requête "rapide" est, bien rapide.
atxdba
Testez les requêtes uniquement avec SQL_NO_CACHE .
mgutt
C'est une question / réponse assez ancienne. Des avancées de mysql / mariadb depuis ces jours?
Roman Susi
1
Attention: Le timing de cette Q&R implique qu'il ne parle que de MyISAM. Son applicabilité à InnoDB est en cause.
Rick James
@RomanSusi - Voulez-vous commencer une nouvelle question destinée à InnoDB?
Rick James

Réponses:

2

Je pense que le problème peut provenir de la présence de l'index FULLTEXT lui-même.

Chaque fois qu'il y a une requête impliquant un index FULLTEXT, MySQL Query Optimizer a tendance à détraquer la requête dans une analyse complète de la table. Je l'ai vu au fil des ans. J'ai également écrit un article précédent sur ce comportement le plus insignifiant dans les index FULLTEXT .

Vous devrez peut-être faire deux choses:

  1. refactoriser la requête afin que l'index FULLTEXT ne jette pas MySQL Query Optimizer dans un état de confusion
  2. Ajoutez un index supplémentaire qui prendra correctement en charge la requête refactorisée

RÉFLÉCHIR LA REQUÊTE

Voici votre requête d'origine

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

Vous devrez refactoriser la requête comme ceci:

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

CRÉER UN NOUVEL INDICE

Vous aurez besoin d'un index à supporter subqueryA. Vous avez déjà un index sur topic_id. Vous devez le remplacer comme suit:

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

Essaie !!!

MISE À JOUR 2012-03-19 13:08 EDT

Essayez celui-ci en premier

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

Si cela s'exécute rapidement et renvoie un petit nombre de lignes, essayez cette sous-requête imbriquée:

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

MISE À JOUR 2012-03-19 13:11 EDT

Comparez le temps d'exécution de ceci:

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

avec ça

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

Si le temps d'exécution est le même, la clause MATCH est exécutée sur chaque ligne. Comme je l'ai mentionné précédemment, l'utilisation des index FULLTEXT tend à annuler tous les avantages tentés et apportés par MySQL Query Optimizer.

RolandoMySQLDBA
la source
Donc, vous voulez dire que ma requête analyse réellement la table entière parce que topic_id et la post_idconfond? Pourquoi la requête LIKE fonctionne-t-elle même sans avoir d'index sur ces colonnes (topic_id, post_id)? Pourquoi MYSQL ne sélectionne-t-il pas simplement intelligemment topic_id = 144017 AND post_id != 155352, puis ne fait-il que naviguer dans ces résultats? Et si 100k lignes incluent ma chaîne de recherche en texte intégral dans post_text? Ne les sélectionnerait-il pas tous?
genesis
En fait, je suis encore plus confus. COMME '% text%' n'utilise pas non plus d'index, cela signifie qu'il scanne toute la table, alors pourquoi est-ce si rapide?
genesis
S'il vous plaît regarder mon ACTUALISATION , je pense que vous allez le résoudre très vite. Je vais vous donner mon représentant si vous le résolvez.
genesis
En réponse à votre deuxième mise à jour. La deuxième requête s'est exécutée en moins de 0,01 ms, la première n'a pas abouti. Pourquoi avez-vous dit "Si le temps d'exécution est le même, alors la clause MATCH est exécutée sur chaque ligne." ? N'est-ce pas exactement le contraire de ce qu'il devrait être? Si vous regardez ici , vous verrez que je ne suis pas le seul à avoir ce problème
genesis
Répondre à votre première mise à jour. La première requête s'est exécutée en 0,01 ms, 0 lignes, la seconde a renvoyé "Impossible de trouver l'index FULLTEXT correspondant à la liste des colonnes". Cependant, votre requête avec 2 sous-requêtes fonctionne parfaitement!
genèse