Warum ist LIKE mehr als 4x schneller als MATCH… GEGEN einen FULLTEXT-Index in MySQL?

12

Ich verstehe das nicht.

Ich habe eine Tabelle mit diesen Indizes

PRIMARY     post_id
INDEX       topic_id
FULLTEXT    post_text

Die Tabelle enthält (nur) 346 000 Zeilen. Ich versuche 2 Abfragen durchzuführen.

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

dauert 4,05 Sekunden während

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

dauert 0,027 Sekunden.

EXPLAIN zeigt, dass der einzige Unterschied in möglichen_ fulltextSchlüsseln besteht ( hat post_text enthalten, LIKEnicht)

Das ist wirklich seltsam.

Was steckt dahinter? Was passiert im Hintergrund? Wie kann LIKEman so schnell sein, wenn man keinen Index verwendet, und FULLTEXT so langsam, wenn man seinen Index verwendet?

UPDATE1:

Eigentlich dauert es jetzt ungefähr 0,5 Sekunden, vielleicht war die Tabelle gesperrt, aber wenn ich die Profilerstellung einschalte, zeigt sich, dass die FULLTEXT-INITIALISIERUNG 0,2 Sekunden gedauert hat. Was geht?

Ich kann meine Tabelle mit LIKE10x pro Sekunde abfragen , mit Volltext nur 2x

UPDATE2:

Überraschung!

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)

Also frage ich, wie ist das möglich?

Zusätzlich,

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

ist sehr langsam. Kann Volltext kaputt sein?

UPDATE3:

Was zum Teufel?

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

dauert 0,27s während

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

dauert mehr als 30 Sekunden! Was läuft hier falsch?

Genesis
quelle
Sind die Antwortzeiten zwischen den beiden über mehrere Läufe hinweg konsistent? Ich bin versucht zu glauben, dass das Zwischenspeichern von Datenträgern ins Spiel kommt, wenn ein erster "langsamer" Test alle benötigten Daten in den RAM lädt, sodass die zweite "schnelle" Abfrage sehr schnell ist.
Atxdba
Testen Sie Abfragen nur mit SQL_NO_CACHE .
mgutt
Das ist eine ziemlich alte Frage / Antwort. Irgendwelche Fortschritte von mysql / mariadb seit diesen Tagen?
Roman Susi
1
Achtung: Der Zeitpunkt dieser Fragen und Antworten impliziert, dass es sich nur um MyISAM handelt. Ihre Anwendbarkeit auf InnoDB ist fraglich.
Rick James
@RomanSusi - Möchten Sie eine neue Frage an InnoDB stellen?
Rick James

Antworten:

2

Ich denke, das Problem kann auf das Vorhandensein des FULLTEXT-Index selbst zurückzuführen sein.

Jedes Mal, wenn eine Abfrage mit einem FULLTEXT-Index vorliegt, führt das MySQL Query Optimizer dazu, dass die Abfrage in einen vollständigen Tabellenscan umgewandelt wird. Ich habe das über die Jahre gesehen. Ich habe auch einen früheren Beitrag über dieses unbedeutendste Verhalten in FULLTEXT-Indizes geschrieben .

Möglicherweise müssen Sie zwei Dinge tun:

  1. Refaktorieren Sie die Abfrage so, dass der FULLTEXT-Index das MySQL Query Optimizer nicht in einen Zustand der Verwirrung versetzt
  2. Fügen Sie einen zusätzlichen Index hinzu, der die überarbeitete Abfrage ordnungsgemäß unterstützt

REFACTOR DIE FRAGE

Hier ist Ihre ursprüngliche Anfrage

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

Sie müssen die Abfrage wie folgt umgestalten:

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

ERSTELLEN SIE EINEN NEUEN INDEX

Zur Unterstützung benötigen Sie einen Index subqueryA. Sie haben bereits einen Index für topic_id. Sie müssen es wie folgt ersetzen:

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

Versuche es !!!

UPDATE 2012-03-19 13:08 EDT

Versuchen Sie dies zuerst

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

Wenn dies schnell ausgeführt wird und eine kleine Anzahl von Zeilen zurückgegeben wird, versuchen Sie diese verschachtelte Unterabfrage:

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

Vergleichen Sie die Laufzeit davon:

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

mit diesem

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

Wenn die Laufzeit gleich ist, wird die MATCH-Klausel in jeder Zeile ausgeführt. Wie ich bereits erwähnt habe, führt die Verwendung von FULLTEXT-Indizes dazu, dass alle vom MySQL Query Optimizer versuchten und bereitgestellten Vorteile zunichte gemacht werden.

RolandoMySQLDBA
quelle
Sie möchten also sagen, dass meine Abfrage tatsächlich die gesamte Tabelle scannt, weil topic_id und sie post_idverwirrt? Warum funktioniert die LIKE-Abfrage auch ohne Index für diese Spalten (topic_id, post_id)? Warum wählt MYSQL topic_id = 144017 AND post_id != 155352diese Ergebnisse nicht einfach intelligent aus und durchsucht sie dann einfach? Und was ist, wenn 100.000 Zeilen meine Volltextsuchzeichenfolge enthalten post_text? Würde es nicht alle auswählen?
Genesis
Eigentlich bin ich noch mehr verwirrt. WIE '% text%' auch keine Indizes verwendet, bedeutet dies, dass die gesamte Tabelle gescannt wird. Warum ist es so schnell?
Genesis
Bitte schauen Sie sich mein UPDATE an , ich denke, Sie werden es sehr schnell lösen. Ich werde Ihnen meinen Repräsentanten geben, wenn Sie es lösen.
Genesis
Antwort auf Ihr zweites Update. Die zweite Abfrage lief in weniger als 0,01 ms, die erste wurde nicht beendet. Warum haben Sie gesagt: "Wenn die Laufzeit gleich ist, wird die MATCH-Klausel in jeder Zeile ausgeführt." ? Ist es nicht genau das Gegenteil von dem, was es sein sollte? Wenn Sie hier schauen , werden Sie sehen, dass ich nicht der einzige mit diesem Problem bin
Genesis
Antwort auf Ihr erstes Update. Die erste Abfrage wurde in 0,01 ms und 0 Zeilen ausgeführt, die zweite gab "FULLTEXT-Index kann nicht mit der Spaltenliste übereinstimmen" zurück. Ihre Abfrage mit 2 Unterabfragen funktioniert jedoch einwandfrei!
Genesis