2016-07-17 4 views
2

私は日本語の辞書を保存しているデータベースを持っています:単語、読み取り値、タグ、タイプ、他の言語での意味(ここでは英語が最も重要ですが、そうです。MySQLのクエリが遅い場所で

Datatables js pluginを使用してインターフェースを作成したいので、ユーザーはテーブルを見て、いくつかのフィルタリングオプションを使用することができます(動詞だけを表示する、または "dog"を含むエントリを見つける)。しかし、フィルタリングを使用するとかなり遅くなるクエリでは苦労しています...私はすでに多くの速度を上げていますが、それでもまだ良くありません。

これが私の基本的なクエリです:

select 
v.id, 
(
    select group_concat(distinct vke.kanji_element separator '; ') from vocabulary_kanji_element as vke 
    where vke.vocabulary_id = v.id 
) kanji_notation, 
(
    select group_concat(distinct vre.reading_element separator '; ') from vocabulary_reading_element as vre 
    where vre.vocabulary_id = v.id 
) reading_notation, 
(
    select group_concat(distinct vsg.gloss separator '; ') from vocabulary_sense_gloss as vsg 
    join vocabulary_sense as vs on vsg.sense_id = vs.id 
    join language as l on vsg.language_id = l.id and l.language_code = 'eng' 
    where vs.vocabulary_id = v.id 
) meanings, 
(
    select group_concat(distinct pos.name_code separator '; ') from vocabulary_sense as vs 
    join vocabulary_sense_has_pos as vshp on vshp.sense_id = vs.id 
    join part_of_speech as pos on pos.id = vshp.pos_id 
    where vs.vocabulary_id = v.id 
) pos 
from vocabulary as v 
join vocabulary_sense as vs on vs.vocabulary_id = v.id 
join vocabulary_sense_gloss as vsg on vsg.sense_id = vs.id 
join vocabulary_kanji_element as vke on vke.vocabulary_id = v.id 
join vocabulary_reading_element as vre on vre.vocabulary_id = v.id 
join language as l on l.id = vsg.language_id and l.language_code = 'eng' 
join vocabulary_sense_has_pos as vshp on vshp.sense_id = vs.id 
join part_of_speech as pos on pos.id = vshp.pos_id 
where 
-- pos.name_code = 'n' and 
(vsg.gloss like '%eat%' OR vke.kanji_element like '%eat%' OR vre.reading_element like '%eat%') 
group by v.id 
order by v.id desc 
-- limit 3900, 25 

出力は次のようなものです:今(私のローカルマシン上で作業)、何のWHERE文はありません場合は、しかし制限で

|id | kanji_notation | reading_notation | meanings | pos | 
--------------------------------------------------------------- 
|117312| お手; 御手 |  おて  | hand; arm |n; int| 

、それは速く働きます - 約0,140秒。しかし、テキストフィルタリングが有効になっている場合、実行時間は最大6.5秒になります。最初にpart_of_speechをフィルタリングすると、その5,5秒のようになります。 3秒は大丈夫ですが、6秒は長すぎます。

テーブルvocabulary_sense_glossに1 155 897のレコードがあるので、それほど多くはないと思います。

CREATE TABLE `vocabulary_sense_gloss` (
    `id` MEDIUMINT(8) UNSIGNED NOT NULL AUTO_INCREMENT, 
    `sense_id` MEDIUMINT(8) UNSIGNED NOT NULL, 
    `gloss` VARCHAR(255) NOT NULL, 
    `language_id` MEDIUMINT(8) UNSIGNED NOT NULL, 
    PRIMARY KEY (`id`), 
    INDEX `vocabulary_sense_gloss_vocabulary_sense_id` (`sense_id`), 
    INDEX `vocabulary_sense_gloss_language_id` (`language_id`), 
    FULLTEXT INDEX `vocabulary_sense_gloss_gloss` (`gloss`), 
    CONSTRAINT `vocabulary_sense_gloss_language_id` FOREIGN KEY (`language_id`) REFERENCES `language` (`id`), 
    CONSTRAINT `vocabulary_sense_gloss_vocabulary_sense_id` FOREIGN KEY (`sense_id`) REFERENCES `vocabulary_sense` (`id`) 
) 
COLLATE='utf8_general_ci' 
ENGINE=InnoDB 
; 

私はそれを最適化する方法はありますか?データベースを変更する必要はありますか?私はフルテキスト検索を使用しようとしていましたが、それほど高速ではなく、完全な条件でしか動作しないようです。 '%eat%'の代わりに 'eat%'を使用すると簡単な物語:私が望むものが返されません。

私は、2つのテーブル(英語のみの用語で、もう1つは残りのもの)でvocabulary_sense_glossを分割しようとしました。とにかくユーザーは通常英語を使用するため、作業が速くなりますが、それが良いアプローチであるかどうかはわかりません。

また、私はVARCHARをCHARに変更しようとしていました。テーブルのサイズが大きく増えたにもかかわらず、実行時間を短縮するように見えました。

答えて

3

このWHERE節のパフォーマンスは非常に悪いです。

(vsg.gloss like '%eat%' OR 
vke.kanji_element like '%eat%' OR 
vre.reading_element like '%eat%') 

なぜですか?まず、column LIKE '%constant%'は、クエリエンジンがcolumnの可能なすべての値を調べるよう要求します。定数検索用語の先頭に%があるため、インデックスを使用できない可能性があります。

第2:OR句は、クエリプランナが結果を3回スキャンする必要があることを意味します。

これを改善するためにはどうしますか?それは容易ではないでしょう。あなたはcolumn LIKE 'constant%'検索用語を使用して定数から先頭の%を削除する方法を理解する必要があります。

あなたは、あなたがこの

 ... 
    WHERE v.id IN 
      (SELECT sense_id AS id 
       FROM vocabulary_sense_gloss 
      WHERE gloss LIKE 'eat%' 
               UNION 
      SELECT vocabulary_id AS id 
       FROM vocabulary_kanji_element 
      WHERE kanji_element LIKE 'eat%' 
               UNION 
      SELECT vocabulary_id AS id 
       FROM vocabulary_reading_element 
      WHERE reading_element LIKE 'eat%' 
      ) 

のような構築物を用いて設定し、あなたの広大な参加結果のトリプルスキャンを打ち負かすことができるかもしれというこれではなく、直接関連する言葉のid数字を引っ張ってくるんたら多方向のJOINの結果から。これを高速にするには、vocabulary_sense_glossに(vocabulary_sense_gloss, sense_id)のインデックスが必要です。他の2つのテーブルにも同様のインデックスが必要です。

関連する問題