1

次のクエリがあります。

SELECT
  b.item_name,
  COUNT(distinct c.user_id) AS total_count,
  AVG(c.item_rating) AS avg_rating
FROM       item_ratings as c
INNER JOIN items AS b ON b.item_id = c.item_id
INNER JOIN users AS u ON u.user_id = c.user_id
WHERE item_active = 1 AND u.user_valid = 1
GROUP BY c.item_id

このクエリは、高度に最適化されたデータベースで 500 秒間実行されます。何が起こっているのかわかりません。

インデックス

item_ratings - item_user_id, (item_id, user_id), item_rating, item_id
users - user_id, user_valid
items - item_id (primary), item_search (item_id, item_name), item_r (parent_id, item_id, item_active) 

テーブルサイズ

item_ratings テーブルは 500 万レコードに近く、items テーブルは約 20 万件、ユーザーは約 25 万件です。

説明

item_active にインデックスがあるにもかかわらず、explain クエリはアイテムのテーブル ソート (20 万行すべてを返す) を行うようです。他のテーブル (item_ratings と user) はどちらも正しいインデックスを使用しています。

アップデート

完全な説明

id  select_type     table   type    possible_keys   key     key_len     ref       rows  Extra
1   SIMPLE  b   ALL     PRIMARY,item_id, item_search, item_r    NULL    NULL    NULL    218419  Using where; Using temporary; Using filesort
1   SIMPLE  c   ref     item_user_id ,user_id, item_id  4   myDB.b.item_id  29  Using where
1   SIMPLE  u   eq_ref  PRIMARY,user_valid,user_id  PRIMARY     4   myDB.c.user_id  1   Using where

ハードウェア これは、Ubuntu 10.10 を実行する専用の MySQL サーバー ボックスで、16GB の RAM を搭載しています。テーブルは MyISAM を実行しています。

助言がありますか?

4

2 に答える 2

2

あなたは正しいです。このクエリに 8 分もかからないはずです。可能性の 1 つは、クエリが完全なテーブル スキャンであるため、インデックスが実際にクエリを悪化させていることです。それらを回避する前に、次のことをお勧めします。

おそらく、users テーブルと items テーブルには個別の ID があります。また、おそらくユーザーは、特定のアイテムに対して 1 つの評価しか持っていません。これが当てはまる場合は、個別のカウントを削除して、カウントに置き換えることができます。

SELECT b.item_name, COUNT(c.user_id) AS total_count, AVG(c.item_rating) AS avg_rating
FROM item_ratings as c INNER JOIN
     items AS b
     ON b.item_id = c.item_id INNER JOIN
     users AS u
     ON u.user_id = c.user_id
WHERE item_active = 1 AND u.user_valid = 1
GROUP BY c.item_id 

次に、「is_active」にインデックスがありません。インデックスはオンです (parent_id、item_id、item_active)。クエリはparent_idを使用していないため、このインデックスは使用されません。

第 3 に、集計のためにアイテム インデックスを経由しているように見えます。item_id ではなく item_name が必要なようですので、グループを次のように変更することをお勧めします。

group by c.item_name

これにより、より優れたクエリ プランを生成できる場合があります。

于 2012-08-27T18:27:41.777 に答える
0

item_active フィールドのインデックスを使用しても、クエリは依然として非常に低速でした。このクエリは 1 日に 1 回しか実行されないため、他のユーザーにも使用できる別のソリューションを見つけました。

基本的に、次のクエリのみを使用して、アクティブなビールのリストを取得しました。

SELECT b.beer_name
FROM items as b
WHERE b.item_active = 1

次に、各行をループして、次のようにアクティブな各アイテムの評価数と平均評価を取得しました。

SELECT COUNT(DISTINCT c.user_id) AS total_count, AVG(c.item_rating) AS avg_rating
FROM item_ratings as c 
INNER JOIN users AS u ON u.user_id = c.user_id
WHERE item_active = 1 AND u.user_valid = 1 and b.item_id = @item_id

@item_id は、私が書いた PHP ループの item_id です。この後、この結果を取得し、クエリ用のテーブルに配置します。この小さなクエリは実行に 1 秒もかからず、オフピーク時に他のテーブルをロックすることなくバッチ スタイル形式で実行できるため、このソリューションは私にとって非常に効果的です。

みんなの提案と助けに感謝します!

于 2012-08-28T13:36:46.373 に答える