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

dba.stackexchange https://dba.stackexchange.com/questions/872

質問

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

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

最初に記事を取得し、次にブログ投稿を取得するのはなぜですか?ブログ投稿のエントリ数が多いからでしょうか?また、記事投稿でインデックスを使用できるようにクエリを改善するにはどうすればよいでしょうか?

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

「」を削除すると、Articles.id AS 記事 ID" 記事の 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
役に立ちましたか?

解決

クエリを詳しく見てみると、再設計できるかもしれません。これが私が言っていることです:

クエリの0.50部分の制限は、最後にクエリで忙しくなっているようです。

以下を実行することにより、クエリのレイアウトを改善できます。

ステップ1)キーのみを収集するインラインクエリを作成します。この場合、BlogPostsのID。

ステップ2)キーを持ち込むインラインクエリの条項で、任意の場所、注文、グループごとにグループ化します。

ステップ3)インラインクエリを作成する最後のステップとしての制限条項を廃止します。

ステップ4)ブロストポストから追加の列が必要な場合に、ブログポストテーブルでインラインクエリに参加してください

ステップ5)記事のテーブルでこの新しいブログ投稿誘惑に参加してください。

手順1-3は、正確に50行で誘惑可能な誘惑を作成し、BlogPost IDを含めることを目的としています。その後、最後にすべてを実行します。

これらの手順が元のクエリに適用されている場合、これが必要です。

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;

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

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が次のように削除され、表示され、date_createdを含むインデックスを作成してください。

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

試してみる !!!

他のヒント

あなたの状態 blogposts.title LIKE '%{de}%' 完全なテーブルスキャンが発生します BlogPosts テーブル。 6915の記事をスキャンするMySQLの数値がより効率的である可能性があります。

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

and blogposts.title like '%{de}%' - 最適化には役に立たない(リーディングワイルドカード)

blogposts.deleted = 0およびblogposts.visible = 1 -yuck:表示すべきでない場合は、テーブルから出してください。

and blogposts.date_published <= now()blogposts.date_created desc limit 0、50- index(date_published)の必要につながる(ただし、類似とフラグはこのインデックスの使用を妨げる場合があります)。

ショーの作成テーブルを提供してください...;テーブルステータスを表示...;

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

試してみたところ、インデックスに関する最後のコメントが私に必要なものだったことがわかりました。場合によっては、小さな改善が必要になることもあります...;) 比較:

古いクエリ:

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

ここで、前述のインデックスを省略すると、次のようになります。

古い Explain 結果:

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 句をサポートするインデックスが必要です。

ライセンス: CC-BY-SA帰属
所属していません dba.stackexchange
scroll top