web-dev-qa-db-ja.com

MySQLのFULLTEXTインデックスでLIKEがMATCH ... AGAINSTより4倍以上速いのはなぜですか?

私はこれを取得していません。

これらのインデックスを持つテーブルがあります

PRIMARY     post_id
INDEX       topic_id
FULLTEXT    post_text

テーブルには(のみ)346 000行があります。 2つのクエリを実行しようとしています。

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

4.05秒かかります

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

0.027秒かかります。

EXPLAINは、可能な違いが唯一の違いであることを示しています(fulltextにはpost_textが含まれていますが、LIKEは含まれていません)。

それは本当に奇妙です。

この背後にあるものは何ですか?バックグラウンドで何が起こっていますか?インデックスを使用していないときにLIKEを高速にして、インデックスを使用しているときにFULLTEXTを低速にするにはどうすればよいですか?

アップデート1:

実際には約0.5秒かかりますが、テーブルがロックされている可能性がありますが、プロファイリングをオンにすると、FULLTEXT INITIALIZATIONに0.2秒かかったことが示されます。調子はどう?

LIKEで1秒あたり10倍、フルテキストは2倍でテーブルをクエリできます

UPDATE2:

驚き!

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)

だから私は尋ねています、これはどのように可能ですか?

さらに、

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

本当に遅いです。全文が壊れている可能性はありますか?

UPDATE3:

なんてこったい?

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

0.27秒かかります

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

30秒以上かかります!ここで何が問題になっていますか?

12
genesis

問題はFULLTEXTインデックス自体の存在に起因していると思います。

FULLTEXTインデックスを含むクエリがあるたびに、MySQLクエリオプティマイザーはクエリをフルテーブルスキャンに変換する傾向があります。私は何年にもわたってこれを見てきました。 FULLTEXTインデックスにおけるこの最も些細な動作についての以前の投稿も書きました

次の2つのことを行う必要がある場合があります。

  1. fULLTEXTインデックスがMySQLクエリオプティマイザーを混乱状態にしないようにクエリをリファクタリングする
  2. リファクタリングされたクエリを適切にサポートする追加のインデックスを追加します

クエリをリファクタリングする

これが元のクエリです

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

次のようにクエリをリファクタリングする必要があります:

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

新しいインデックスを作成する

subqueryAをサポートするにはインデックスが必要です。 topic_idにはすでにインデックスがあります。次のように交換する必要があります。

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

試してみる !!!

UPDATE 2012-03-19 13:08 EDT

まずこれを試してください

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

これが高速で実行され、少数の行を返す場合は、次のネストされたサブクエリを試してください。

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

この実行時間を比較します。

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

これとともに

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

実行時間が同じ場合、MATCH句はすべての行で実行されています。前に述べたように、FULLTEXTインデックスを使用すると、MySQLクエリオプティマイザーによって試行および提供された利点がすべて無効になる傾向があります。

2
RolandoMySQLDBA