2016-04-28 31 views
0

私は、クエリを持っている:最適化MySQLのクエリ

SELECT items.* 
FROM items 
INNER JOIN other on other.some_key = items.some_key 
WHERE 
    items.partition_id = 7 AND 
    items.created_date > '2016-01-01 00:00:00' AND 
    (
    other.name like '%user query%' OR 
    match (fulltext_candidate) against ('+user* +query*' IN BOOLEAN MODE) OR 
    items.some_varchar like '%user query%' 
) 
ORDER BY items.created_date 
LIMIT 20 OFFSET 0; 

私は、クエリが遅い複数のインデックスに

ALTER TABLE `items` ADD INDEX `partition_id` (`partition_id`) 
ALTER TABLE `items` ADD FULLTEXT `fti` (`fulltext_candidate`) 
ALTER TABLE `items` ADD INDEX `created_date` (`created_date`) 
ALTER TABLE `items` ADD INDEX `some_varchar` (`some_varchar`) 
ALTER TABLE `other` ADD INDEX `name` (`name`) 

を追加しました。低速クエリログには、itemsテーブルのすべての行をスキャンするクエリが表示されます。私は、MySQLがクエリで1つのインデックスしか使用していないと考えています。 WHERE句に1つの列のみを含める縮小クエリを実行すると、クエリは非常に高速です。

フルテキストインデックスと通常インデックスの両方を使用してこのクエリを最適化する単一のインデックスを作成するにはどうすればよいですか?

+0

どのように遅いですか?どのような種類のテーブルを実行していますか?クエリはどのくらい時間がかかりますか? –

+0

クエリは1分以上かかる可能性があります。テーブルはInnoDBです。 – Dave

答えて

0

これはコメントには大きすぎます。

これは奇妙に見えます。

inner join other on other.some_key = items.some_key 

some_keyがインデックスされていて、非常に奇妙な名前を持っているとしますか?そうであれば、指定されたクエリのインデックスにヒットしていない場合は、データを取得するための最速の方法であるため、フルテーブルスキャンを実行している可能性があります。あなたのデータなどについてもっと知る必要があります。例として。小さなテーブルでは、データベースは数キロバイトのデータであるため、フルテーブルでスキャンする可能性があり、複数の方法でクエリにアクセスできます。オプティマイザはインデックスを参照して、結果セットをフィルタリングできる量を呼び出します。この数値が低い場合はオプティマイザはそれを使用しません。

+0

はい、テーブルを結合するために使用されたキーは索引付けされていますが、ここでは明確ではありません。 – Dave

0

"other.some_key"と "items.somekey"(結合列)は必ず索引付けする必要があります。

5.0より前のバージョンのMySQLでは、クエリごとに1つのインデックスしか使用できません。 explainはこの場合に起こっていることを教えてくれます。

インデックスを使用しませんクエリでの基準がいくつかありますに関係なく、あなたが何をすべきか:

other.name like '%user query%' 
items.some_varchar like '%user query%' 

が有力ワイルドカードを持っている、とインデックスはあなたが変換することができます(あなたは良いしないだろう代わりにテキスト検索をフリーズしますか?)。これが原因で発生したパフォーマンスがどれだけ高いのかは明らかではありません。クエリの残りの部分が一致するレコードの数を数百または数千に絞り込んでも差はありません。再度、EXPLAINが助けになります。

先頭のワイルドカードを削除して、複合インデックスに「some_varchar」を追加することができます。

ご質問のために、複合インデックスを1つ作成します。その方法では、オプティマイザは次に推測しません。

ALTER TABLE `items` ADD INDEX `compound` (some_key, partition_id, created_date) 
+0

other.some_keyおよびitems.somekeyのインデックスについては正しいです。 今すぐMySQL 5.0にアップグレードするオプションがありません LIKEを使用するクエリの2つの条件について、先行するワイルドカードを削除するとパフォーマンスが向上しますか? other.nameとitems.some_varcharをフルテキストインデックスを使用するように変換すると、複合インデックスに追加できますか? 一部の列に化合物索引にFULLTEXT索引を含めることはできますか?そうでない場合、MySQLは1つのインデックスしか使用できません。複合インデックスと全文インデックスのどちらかを選択する必要がありますか? – Dave

+0

明確にするために回答を更新しました。詳細を知りたい場合は、EXPLAINを投稿してください。私が提案した複合インデックスを試してみてください。 –

+0

更新していただきありがとうございます。私はMySQL 5.xを実際に使っていることに気付きました。それは潜在的にあなたの提案を変えるでしょうか? – Dave

0

回答

はこれらを持っています。何も他の人があなたのクエリ(または任意の議論の変異体)のために有用であるんでしょう。他のすべてのおしゃべりに

items: INDEX(partition_id, created_date) 
other: INDEX(some_key) 

コメント

ORsは列の(例えば、あなたのFULLTEXTなど)のインデックスのいずれかの使用を防止しますそれらに言及した。つまり、先頭の%を取り除くことは、ORのためにうまくいかないでしょう。

(some_key, partition_id, created_date)は、WHEREORDER BYで使用できなくなります。

「索引マージ」を使用することはできますが、まれにsoooooが呼び出されて考慮する価値がないことがあります。

FULLTEXTインデックスを使用できるMATCH ...インデックスを使用している場合は、FULLTEXTインデックスのみが使用されます。つまり、の場合はとなります。繰り返しますが、ORはあなたのケースでそれを防止します。確かに、 "複合"インデックスは非常に便利です。オプティマイザは常にFULLTEXTがより有用であると仮定します。

FULLTEXTは使用できないため、オプティマイザはINDEX(partition_id, created_date)に移動して便利です。