Nechápu to.
Mám tabulku s těmito indexy
PRIMARY post_id
INDEX topic_id
FULLTEXT post_text
Tabulka obsahuje (pouze) 346 000 řádků. Snažím se provést 2 dotazy.
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')
trvá 4,05 sekund
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%')
trvá 0,027 sekundy.
EXPLAIN ukazuje, že jediným rozdílem je možný_keys (fulltext
obsahuje post_text, LIKE
ne)
To je opravdu divné.
Co je za tím? Co se děje v pozadí? Jak může být LIKE
tak rychlý, když nepoužíváme index a FULLTEXT tak pomalý, když používám jeho index?
Ve skutečnosti to nyní trvá asi 0,5 sekundy, možná byl stůl zamčený, ale přesto, když zapnu profilování, ukazuje se, že FULLTEXT INITIALIZATION trvalo 0,2 sekundy. Co se děje?
Mohu dotazovat svou tabulku s LIKE
10x za sekundu, s fulltextem pouze 2x
Překvapení!
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)
tak se ptám, jak je to možné?
Dodatečně,
SELECT count(*) FROM phpbb_posts WHERE MATCH(post_text) AGAINST ('rapidshare.com')
je opravdu pomalý. Může být fulltext rozbitý?
Co to k sakru?
SELECT forum_id, post_id, topic_id, post_text FROM phpbb_posts WHERE MATCH(post_text) AGAINST ('rapidshare.com') LIMIT 0, 30;
trvá 0,27 s
SELECT count(*) FROM phpbb_posts WHERE MATCH(post_text) AGAINST ('rapidshare.com') LIMIT 0, 30;
trvá déle než 30 sekund! Co se tady děje?
Myslím, že problém může pramenit z přítomnosti samotného indexu FULLTEXT.
Pokaždé, když existuje dotaz týkající se indexu FULLTEXT, nástroj MySQL Query Optimizer inklinuje dotaz na dotaz do skenování v plné tabulce. Viděl jsem to v průběhu let. Také jsem psal dřívější příspěvek o tomto nejmilnějším chování v indexech FULLTEXT .
Možná budete muset udělat dvě věci:
Zde je váš původní dotaz
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')
Dotaz budete muset změnit takto:
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);
Pro podporu subqueryA
budete potřebovat index. Již máte index na topic_id
. Musíte ji nahradit následovně:
ALTER TABLE phpbb_posts ADD INDEX topic_post_ndx (topic_id,post_id);
ALTER TABLE phpbb_posts DROP INDEX topic_id;
Pokusit se !!!
Zkuste to první
SELECT post_id FROM
(
SELECT * FROM phpbb_posts
WHERE topic_id = 144017
AND post_id != 155352
) A;
Pokud to běží rychle a vrací malý počet řádků, zkuste tento vnořený poddotaz:
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');
Porovnejte provozní dobu:
SELECT count(*) FROM phpbb_posts WHERE MATCH(post_text) AGAINST ('rapidshare.com') LIMIT 0, 30;
s tím
SELECT count(*) FROM phpbb_posts WHERE 1 = 1;
Pokud je doba běhu stejná, pak se klauzule MATCH provádí na každém řádku. Jak jsem zmínil dříve, použití indexů FULLTEXT má tendenci anulovat všechny výhody, o které se pokoušel a přispěl Optimalizátor dotazů MySQL.