1

フレンド フィード クエリで奇妙な問題が発生しています。背景は次のとおりです。

私は3つのテーブルを持っています

checkin - around 13m records
users - around 250k records
friends - around 1.5m records

チェックイン テーブル - ユーザーによって実行されたアクティビティが一覧表示されます。(ここには多数のインデックスがありますが、user_id、created_at、および(user_id、created_at)にインデックスがあります。users テーブルは基本的なユーザー情報です。user_id にインデックスがあります。friends テーブルには、user_id、target_id、および is_approved があります。 (user_id, is_approved) フィールドにインデックスがあります。

私のクエリでは、任意のユーザーの基本的な友達フィードだけをプルダウンしようとしています - だから私はこれをやっています:

SELECT checkin_id, created_at
FROM checkin
WHERE (user_id IN (SELECT friend_id from friends where user_id = 1 and is_approved = 1) OR user_id = 1)
ORDER by created_at DESC
LIMIT 0, 15

クエリの目的は、すべてのユーザーのフレンドとそのアクティビティの checkin_id と created_at を取得することです。これは非常に単純なクエリですが、ユーザーの友人が最近のアクティビティを大量に持っている場合、このクエリは非常に高速です。EXPLAIN は次のとおりです。

 id     select_type     table   type    possible_keys   key     key_len     ref     rows    Extra
 1  PRIMARY     checkin     index   user_id,user_id_2   created_at  8   NULL    15  Using where
 2  DEPENDENT SUBQUERY friends  eq_ref    user_id,friend_id,is_approved,friend_looku...     PRIMARY     8   const,func  1   Using where

説明として、user_id は user_id の単純なインデックスですが、user_id_2 は user_id と created_at のインデックスです。friends テーブルでは、friends_lookup は user_id と is_approved のインデックスです。

これは非常に単純なクエリで、get は次のように完了します。

ただし、ユーザーの友達のアクティビティが最近のものではなく、データがあまりない場合、同じクエリは約 5 ~ 7 秒かかり、前のクエリと同じ EXPLAIN になりますが、時間がかかります。

より多くの友人に影響を与えているようには見えませんが、最近のアクティビティでスピードアップしているようです.

これらのクエリを最適化して、アクティビティに関係なく同じ速度で実行されるようにするためのヒントはありますか?

サーバーのセットアップ

これは、16 GB の RAM を実行する専用の MySQL サーバーです。Ubuntu 10.10 を実行しており、MySQL のバージョンは 5.1.49 です。

アップデート

したがって、ほとんどの人は、IN 部分を削除して INNER JOIN に移動することを提案しています。

SELECT c.checkin_id, c.created_at
FROM checkin c
INNER JOIN friends f ON c.user_id = f.friend_id
WHERE f.user_id =1
AND f.is_approved =1
ORDER BY c.created_at DESC
LIMIT 0 , 15

EXPLAIN で報告されているように、このクエリは 10 倍悪いです。

 id     select_type     table   type    possible_keys   key     key_len     ref     rows    Extra
 1  SIMPLE  f   ref     PRIMARY,user_id,friend_id,is_approved,friend_looku...   friend_lookup   5   const,const     938     Using temporary; Using filesort
 1  SIMPLE  c   ref     user_id,user_id_2   user_id     4   untappd_prod.f.friend_id    71  Using where

このクエリの目的は、すべての友達のアクティビティと自分のアクティビティを同じクエリで取得することです (2 つのクエリを作成して結果をマージし、created_at で並べ替える必要はありません)。また、別のクエリの重要な部分であるため、user_id のインデックスを削除することもできません。

興味深いのは、アクティビティがあまりないユーザー アカウントでこのクエリを実行すると、次のような説明が得られることです。

 id     select_type     table   type    possible_keys   key     key_len     ref     rows    Extra
 1  SIMPLE  f   index_merge     PRIMARY,user_id,friend_id,is_approved,friend_looku...    user_id,friend_lookup  4,5     NULL    11  Using intersect(user_id,friend_lookup); Using wher...
 1  SIMPLE  c   ref     user_id,user_id_2   user_id     4   untappd_prod.f.friend_id    71  Using where

何かアドバイス?

4

2 に答える 2

2

だから..ここで起こっていることがいくつかあります..

  1. Explainプランでは..通常、オプティマイザは、possible_keysのwhatsではなく、"key"のwhatsを選択します。そのため、データが最近のものではないときに、より多くのレコードをスキャンする必要がある場合に経験します。

  2. チェックインテーブルでのみ(user_id、created_at)およびcreated_atが必要です。user_idに別のインデックスは必要ありません。user_idは最初のオーダーであるため、オプティマイザは(user_id、created_at)を使用します。

これを試して..

  1. 友達同士の参加とチェックインを使用し、in句を削除して、友達が駆動テーブルになり、Explainプランの実行パスで最初にそれを確認する必要があります。

  2. 1が完了したら、チェックインが実行パスで(user_id、created_dt)インデックスを使用していることを確認する必要があります。

  3. チェックインテーブルのuser_idが1であるOR条件に対して別のクエリを記述します。データセットはこれら2つのセットに対して相互に排他的である必要があり、それで問題ないはずです。そうでない場合は、INの後にOR条件を設定する必要はありません。そもそも節。

  4. user_id、created_atインデックスがあるので、それ自体でuser_idインデックスを削除します。

--目標は、可能なキーだけでなく、キーの下のインデックスを使用することです。

これにより、最近のチェックインだけでなく、古いチェックイン以外のチェックインも処理されます。

于 2012-09-24T15:52:06.907 に答える
0

私の最初の提案は、依存サブクエリを削除して結合に変換することです。MySQLはこれらのタイプのクエリの処理が苦手であることがわかりました。これを試して:

SELECT c.checkin_id, c.created_at
FROM checkin c
INNER JOIN friends f
   ON c.user_id = f.friend_id
WHERE f.user_id = 1
   AND f.is_approved = 1
ORDER by c.created_at DESC
LIMIT 0, 15

2番目の提案は、専用サーバーがあるため、すべてのテーブルにInnoDBストレージエンジンを使用することです。特にinnodb_buffer_pool_sizeの場合は、デフォルトのInnoDB設定を調整してください:http ://www.mysqlperformanceblog.com/2007/11/03/choosing-innodb_buffer_pool_size/

于 2012-09-24T15:51:19.767 に答える