it-swarm-eu.dev

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

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 Schlüsseln besteht (fulltext enthält Posttext, LIKE nicht)

Das ist wirklich seltsam.

Was steckt dahinter? Was passiert im Hintergrund? Wie kann LIKE so schnell sein, wenn kein Index verwendet wird, und FULLTEXT so langsam, wenn der Index verwendet wird?

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. Wie geht's?

Ich kann meine Tabelle mit LIKE 10x 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?

12
genesis

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

Jedes Mal, wenn eine Abfrage einen FULLTEXT-Index enthält, 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

Sie benötigen einen Index, um subqueryA zu unterstützen. 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.

2
RolandoMySQLDBA