web-dev-qa-db-ja.com

MySQL:使用されていないテーブルに結合するときのインデックス(パフォーマンス最適化の質問)

次のクエリを最適化する必要があります。

SELECT /* [things omitted] */ articles.blogpost_id, articles.id AS articleid
FROM blogposts
JOIN articles ON articles.blogpost_id = blogposts.id
WHERE blogposts.deleted = 0
AND blogposts.title LIKE '%{de}%'
AND blogposts.visible = 1
AND blogposts.date_published <= NOW()
ORDER BY blogposts.date_created DESC
LIMIT 0 , 50

EXPLAIN SELECTを実行すると、次の結果が得られます。

id select_type table type possible_keys key key_len ref rows Extra
1 SIMPLE articles ALL blogpost_id NULL NULL NULL 6915 Using temporary; Using filesort
1 SIMPLE blogposts eq_ref PRIMARY PRIMARY 4 articles.blogpost_id 1 Using where

なぜ最初に記事、次にブログ記事が表示されるのですか?ブログ投稿のエントリが多いからですか?そして、記事投稿がインデックスを使用できるように、クエリをどのように改善できますか?

pdate: blogposts.date_createdにインデックスが設定されます。 blogposts.title LIKE条件とdate_published <= NOW()を削除しても何も起こりません。

"articles.id AS articleid"を削除すると、記事のblogpost_idインデックスを使用できます...私には奇妙に聞こえますが、誰かが理由を知っていますか? (私は実際にそれを必要とするので..)

新しい説明は次のようになります。

id  select_type table   type    possible_keys   key key_len ref rows    Extra
1   SIMPLE  articles    index blogpost_id blogpost_id    4    NULL    6915    Using index; Using temporary; Using filesort
1   SIMPLE  blogposts   eq_ref  PRIMARY PRIMARY 4   articles.blogpost_id    1   Using where
6
Michael Weibel

クエリを詳しく調べたところ、あなたはそれを再設計できるかもしれません。これが私の意味です:

クエリのLIMIT 0,50部分は、最後のクエリでビジー状態になっているようです。

次の操作を行うことにより、クエリのレイアウトを改善できます。

手順1)キーのみを収集するインラインクエリを作成します。この場合、ブログ投稿のID。

ステップ2)キーをもたらすインラインクエリにWHERE、ORDER BY、GROUP BY句を適用します。

手順3)インラインクエリを作成する最後の手順として、LIMIT句を挿入します。

ステップ4)ブログポストの追加の列がブロストポストのテンプレートとして必要な場合は、インラインクエリをブログポストテーブルと結合します。

手順5)この新しいブログ投稿をアーティクルテーブルと結合します。

手順1〜3は、正確に50行のテンプレートを作成し、ブログ投稿IDを含めることを目的としています。次に、すべてのJOINを最後に実行します。

これらの手順を元のクエリに適用すると、次のようになります。

SELECT /* [things omitted] */ articles.blogpost_id, articles.id AS articleid
FROM
(
  SELECT B.*
  FROM
  (
    SELECT id FROM blogposts
    WHERE date_published <= NOW()
    AND deleted = 0 AND visible = 1
    AND title LIKE '%{de}%'
    ORDER BY date_created DESC
    LIMIT 0,50
  ) A
  INNER JOIN blogposts B USING (id)
) blogposts
INNER JOIN articles
ON blogposts.id = articles.blogpost_id;

質問を編集して、LIKEを削除すると述べたので、クエリは次のようになります。

SELECT /* [things omitted] */ articles.blogpost_id, articles.id AS articleid
FROM
(
  SELECT B.*
  FROM
  (
    SELECT id FROM blogposts
    WHERE date_published <= NOW()
    AND deleted = 0 AND visible = 1
    ORDER BY date_created DESC
    LIMIT 0,50
  ) A
  INNER JOIN blogposts B USING (id)
) blogposts
INNER JOIN articles
ON blogposts.id = articles.blogpost_id;

[省略されたもの]というフレーズで、キー以外にブログ投稿から何も必要ない場合、クエリは次のようになります。

SELECT /* [things omitted] */ articles.blogpost_id, articles.id AS articleid
FROM
(
  SELECT id FROM blogposts
  WHERE date_published <= NOW()
  AND deleted = 0 AND visible = 1
  ORDER BY date_created DESC
  LIMIT 0,50
) blogposts
INNER JOIN articles
ON blogposts.id = articles.blogpost_id;

警告

次のように、削除、表示、およびdate_createdの列を含むインデックスを作成してください。

ALTER TABLE blogposts ADD INDEX deleted_visible_date_created (deleted,visible,date_created);

試してみる !!!

4
RolandoMySQLDBA

Where条件blogposts.title LIKE '%{de}%'blogpostsテーブルで全テーブルスキャンを実行します。 MySQLの数値が6915の記事をスキャンする方が効率的である可能性があります。

それを改善する方法として、date_createdまたはdate_publishedを使用してblogpostsにインデックスを追加し、where条件に範囲を追加することができます(<= NOW以外のもの) ())

4
Derek Downey

AND blogposts.title LIKE '%{de}%'-最適化には役に立たない(先頭のワイルドカード)

WHERE blogposts.deleted = 0 AND blogposts.visible = 1-残念:表示する必要がない場合は、テーブルから除外してください。

AND blogposts.date_published <= NOW()ORDER BY blogposts.date_created DESC LIMIT 0、50-その結果、INDEX(date_published)が必要になります(ただし、LIKEとフラグにより​​、このインデックスは使用できなくなる場合があります)。

SHOW CREATE TABLE ...;を指定してください。表のステータスを表示...;

3
Rick James

どうもありがとうございました :)

私はそれを試してみましたが、インデックスに関する実際の最後のコメントが実際に必要なものであることがわかりました。時々、それは必要とされるほんの少しの改善です...;)比較:

古いクエリ:

SELECT /* [things omitted] */ articles.blogpost_id, articles.id AS articleid
FROM blogposts
JOIN articles ON articles.blogpost_id = blogposts.id
WHERE blogposts.deleted = 0
AND blogposts.title LIKE '%{de}%'
AND blogposts.visible = 1
AND blogposts.date_published <= NOW()
ORDER BY blogposts.date_created DESC
LIMIT 0 , 50

新しいクエリ:

SELECT /* [things omitted] */ articles.blogpost_id, articles.id AS articleid
FROM
(
    SELECT B.*
    FROM
    (
        SELECT id FROM blogposts
        WHERE date_published <= NOW()
        AND deleted = 0 AND visible = 1
        AND title LIKE '%{de}%'
        ORDER BY date_created DESC
        LIMIT 0,50
    ) A
    INNER JOIN blogposts B USING (id)
) blogposts
INNER JOIN articles
ON blogposts.id = articles.blogpost_id

今言及されたインデックスなし:

古い説明の結果:

id select_type table type possible_keys key key_len ref rows Extra
1 SIMPLE articles ALL blogpost_id NULL NULL NULL 6915 Using temporary; Using filesort
1 SIMPLE blogposts eq_ref PRIMARY PRIMARY 4 articles.blogpost_id 1 Using where

新しい説明結果:

id  select_type     table   type    possible_keys   key     key_len     ref     rows    Extra
1   PRIMARY     <derived2>  ALL     NULL    NULL    NULL    NULL    50   
1   PRIMARY     articles    ref     blogposts_id    blogposts_id    4   blogposts.id    1    
2   DERIVED     <derived3>  ALL     NULL    NULL    NULL    NULL    50   
2   DERIVED     B   eq_ref  PRIMARY     PRIMARY     4   A.id    1    
3   DERIVED     blogposts   ALL     deleted,visible,date_published  deleted     1       28198   Using filesort

古いクエリのインデックスで結果を説明:

id  select_type     table   type    possible_keys   key     key_len     ref     rows    Extra
1   SIMPLE  blogposts   ref     PRIMARY,deleted,visible,date_published,deleted_vis...   deleted_visible_date_created    2   const,const     27771   Using where
1   SIMPLE  articles    ref     blogposts_id    blogposts_id    4   db.blogposts.id     1

新しいクエリのインデックスで結果を説明:

id  select_type     table   type    possible_keys   key     key_len     ref     rows    Extra
1   PRIMARY     <derived2>  ALL     NULL    NULL    NULL    NULL    50   
1   PRIMARY     articles    ref     blogposts_id    blogposts_id    4   blogposts.id    1    
2   DERIVED     <derived3>  ALL     NULL    NULL    NULL    NULL    50   
2   DERIVED     B   eq_ref  PRIMARY     PRIMARY     4   A.id    1    
3   DERIVED     blogposts   ref     deleted,visible,date_published,deleted_visible_dat...   deleted_visible_date_created    2       27771   Using where

インデックスなし/ありの古いクエリの速度: 0.1835/0.0037

インデックスなし/ありの新しいクエリの速度: 0.1883/0.0035

したがって、古いクエリと新しいクエリのわずかな違いのため、古いクエリを使用することをお勧めしますが、インデックスを使用します。しかし、いつか古いクエリが遅すぎるかのように、これを覚えておきます:)

私にとって興味深いのは、どのようにしてこのようにインデックスを設定するアイデアを得たのかを知ることです。私はこの質問を公開したときに、deleted_visibleも試しましたが、date_createdの代わりにdate_publishedを使用しました(where句にあるため)...

ありがとう:)

RolandoMySQLDBA 2011-05-19 13:03による更新

私にそれを与えたのは、WHERE句とORDER BY句でした。

WHERE句では、削除済み(0)および表示可能(1)は静的な値です。 ORDER BY句では、date_createdはdeleted = 0およびvisible = 1のすべての行の間で移動するターゲットのようでした。したがって、静的変数を最初にインデックスの前に配置し、次に移動ターゲットをインデックスの最後に配置しました。通常、これはSQLステートメントをリファクタリングする基本的な原則の一部です。 WHERE、GROUP BY、およびORDER句をサポートするインデックスが必要です。

2
Michael Weibel