0

次のクエリを最適化する方法はありますか:

EXPLAIN EXTENDED SELECT keyword_id, ck.keyword, COUNT( article_id ) AS cnt
FROM career_article_keyword
LEFT JOIN career_keywords ck
USING ( keyword_id ) 
WHERE keyword_id
IN (

SELECT keyword_id
FROM career_article_keyword
LEFT JOIN career_keywords ck
USING ( keyword_id ) 
WHERE article_id
IN (

SELECT article_id
FROM career_article_keyword
WHERE keyword_id =9
)
AND keyword_id <>9
)
GROUP BY keyword_id
ORDER BY cnt DESC

ここでの主なタスクは、特定の keyword_id (CURRENT_KID) がある場合、CURRENT_KID と共に記事に属していたすべてのキーワードを検索し、これらのキーワードの使用量に基づいて結果を並べ替える必要があります。

次のように定義されたテーブル:

mysql> show create table career_article_keyword;
+------------------------+------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+
| Table                  | Create Table                                                                                                                                                                                                                                                                                                                                               |
+------------------------+------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+
| career_article_keyword | CREATE TABLE `career_article_keyword` (
  `article_id` int(11) unsigned NOT NULL,
  `keyword_id` int(11) NOT NULL,
  UNIQUE KEY `article_id` (`article_id`,`keyword_id`),
  CONSTRAINT `career_article_keyword_ibfk_1` FOREIGN KEY (`article_id`) REFERENCES `career` (`menu_id`) ON DELETE CASCADE ON UPDATE CASCADE
) ENGINE=InnoDB DEFAULT CHARSET=utf8 |
+------------------------+------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+
1 row in set (0.00 sec)

mysql> show create table career_keywords;
+-----------------+----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+
| Table           | Create Table                                                                                                                                                                                                         |
+-----------------+----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+
| career_keywords | CREATE TABLE `career_keywords` (
  `keyword_id` smallint(5) unsigned NOT NULL AUTO_INCREMENT,
  `keyword` varchar(250) NOT NULL,
  PRIMARY KEY (`keyword_id`)
) ENGINE=InnoDB AUTO_INCREMENT=15 DEFAULT CHARSET=utf8 |
+-----------------+----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+
1 row in set (0.00 sec)

「explain」の出力が怖い

http://o7.no/J6ThIs

ビッグデータでは、このクエリはすべてを殺す可能性があります:)どうにかして高速化できますか?

ありがとう。

4

1 に答える 1

2

あなたのEXPLAIN出力を見て、サブクエリを使用した結果、インデックスが最適に使用されていないことが懸念されました。私(何の正当性もなく、これについては間違っているかもしれませんが)使用して書き換えるJOINと、より最適化されたクエリにつながる可能性があると感じました。

そのためには、クエリの意図を理解する必要があります。あなたの質問がそれを明確にしていれば助けになったでしょうが、少し頭を悩ませた後、あなたのクエリは、特定のキーワードを含む記事に表示される他のすべてのキーワードのリストと、すべての記事の数を取得しようとしていると判断しましたそれらのキーワードが表示されます。

次に、クエリを段階的に再構築しましょう。

  1. 「特定のキーワードを含む任意の記事」を取得します(重複は気にしません):

    SELECT ca2.article_id
    FROM
           career_article_keyword AS ca2
    WHERE
          ca2.keyword_id = 9;
    
  2. 「 [the above] に現れる他のすべてのキーワード」を取得する

    SELECT ca1.keyword_id
    FROM
           career_article_keyword AS ca1
      JOIN career_article_keyword AS ca2 ON (ca2.article_id = ca1.article_id)
    WHERE
          ca1.keyword_id <> 9
      AND ca2.keyword_id =  9
    GROUP BY ca1.keyword_id;
    
  3. " [the above] と、それらのキーワードが含まれるすべての記事の数を取得します"

    SELECT ca1.keyword_id, COUNT(DISTINCT ca0.article_id) AS cnt
    FROM
           career_article_keyword AS ca0
      JOIN career_article_keyword AS ca1 USING (keyword_id)
      JOIN career_article_keyword AS ca2 ON (ca2.article_id = ca1.article_id)
    WHERE
          ca1.keyword_id <> 9
      AND ca2.keyword_id =  9
    GROUP BY ca1.keyword_id
    ORDER BY cnt DESC;
    
  4. 最後に、一致するキーワード自体をcareer_keywordテーブルから出力に追加します。

    SELECT ck.keyword_id, ck.keyword, COUNT(DISTINCT ca0.article_id) AS cnt
    FROM
           career_keywords        AS ck 
      JOIN career_article_keyword AS ca0 USING (keyword_id)
      JOIN career_article_keyword AS ca1 USING (keyword_id)
      JOIN career_article_keyword AS ca2 ON (ca2.article_id = ca1.article_id)
    WHERE
          ca1.keyword_id <> 9
      AND ca2.keyword_id =  9
    GROUP BY ck.keyword_id -- equal to ca1.keyword_id due to join conditions
    ORDER BY cnt DESC;
    

すぐにわかることの 1 つは、元のクエリがcareer_keywords2 回参照しているのに対し、この書き直されたクエリはそのテーブルを 1 回しか参照していないことです。これだけでパフォーマンスの違いを説明できるかもしれません - それへの 2 番目の参照 (つまり、最初のサブクエリに現れる場所) を削除してみてください。そこでは完全に冗長です。

このクエリを振り返ると、次の列で結合が実行されていることがわかります。

  • career_keywords.keyword_idck JOIN ca0

    このテーブルは を定義しPRIMARY KEY (`keyword_id`)ているため、この結合に使用できる適切なインデックスがあります。

  • career_article_keyword.article_idca1 JOIN ca2

    このテーブルは定義UNIQUE KEY `article_id` (`article_id`,`keyword_id`)し、article_idこのインデックスの左端の列であるため、この結合に使用できる適切なインデックスがあります。

  • career_article_keyword.keyword_idck JOIN ca0と_ca0 JOIN ca1

    この結合に使用できるインデックスはありません。このテーブルで定義されている唯一のインデックスarticle_idには、左側に別の列があります。keyword_idそのため、MySQL はkeyword_id最初にarticle_id. keyword_id一番左の列を持つ新しいインデックスを作成することをお勧めします。

    (このインデックスの必要性は、最も外側の 2 つのクエリがその列に対して結合を実行する元のクエリを直接確認することで同様に確認できたはずです。)

于 2012-04-24T13:28:01.567 に答える