2009-10-20 4 views
9

私は80,000以上のレコードを持つテーブルを持っています。これはシステムと呼ばれます。私はまた別のテーブルを持っています。私のmysqlステートメントを最適化! - RAND()TOO SLOW

システムテーブルからレコードをランダムに選択するには、私のステートメントが必要です。このIDは、現行のユーザーIDの下のテーブルの下にまだリストされていません。だからここ

は私が持っているものです。

SELECT system.id, 
      system.username, 
      system.password, 
      system.followed, 
      system.isvalid, 
      follows.userid, 
      follows.systemid 
     FROM system 
    LEFT JOIN follows ON system.id = follows.systemid 
        AND follows.userid = 2 
     WHERE system.followed = 0 
     AND system.isvalid = 1 
     AND follows.systemid IS NULL 
    ORDER BY RAND() 
     LIMIT 200 

は、今ではそれもそれが選択したレコードを持つ手でジョブの処理を開始する前に、それは全体分ほどかかりますことを除いて、完全にwotks。この時間までにスクリプトは通常ooutを実行し、何も起こりません。

誰かが私にこれを再加工する方法を示すことができるので、同じ考えが行われますが、それはrandによる注文を使用していませんか?これは物事を一気に減速させるようです。

ありがとうございます!

+1

JOINフィールドにはどのようなインデックスがありますか?それは大きなボトルの首になることができます。 – dnagirl

+0

あなたは何を意味するかわからない... – Brandon

+0

@Brandonこれについて少し遅れていることは分かっていますが、これを行うための半単純化した方法をお望みなら、サブクエリに入れることができます。詳細はこちらhttp://stackoverflow.com/questions/25361158/mysql-select-random-on-large-table-order-by-score/25364339?noredirect=1#comment39644652_25364339 –

答えて

7

あなたのクエリを置き換える簡単な解決策があるかどうかはわかりませんが、ここでこのタイプの問題を修正する記事があります。

http://www.titov.net/2005/09/21/do-not-use-order-by-rand-or-how-to-get-random-rows-from-table/

+0

ありがとう、しかし、その方法のための実行可能なオプションではないこのクエリは機能します。 – Brandon

+0

なぜですか?その記事にはさまざまな解決策がありますが、そのうちのいくつかはあなたのために働くと思います。 IDフィールドはオートインクリメントフィールドですか?そうであれば、ランダムなIDを選択するソリューションが機能するはずです。 –

2

あなたはIDと現在の時刻に基づいていくつかの擬似乱数値を生成することができます。

ORDER BY 37*(UNIX_TIMESTAMP()^system.id) & 0xffff 

は、IDから刺されをミックスし、その後16

のみ最低かかります
+0

ちょっと遅くなっているようです... – Brandon

2

遅さの主な理由は2つあります。

  • SQLが最初に必要です行
  • のそれぞれの行は、このような状況を助けるためにトリックがあり、トップ200のもの

を選択するには、この番号に基づいて発注されなければならないため、ランダムな番号を発行し、それが少しを必要とします準備作業とそれを実装する方法(およびその相対的関心)は、実際のユースケースによって異なります。

==>アイデアがで、一度準備時に、ランダムに割り当てられた値を持つ整数値の列を持つことである

フィルタリングアウトするほとんどの行を「ランダムカテゴリ」の値で余分な列を導入0と9の間の値(または1と25 ...何でも)。この列は、クエリで使用されるインデックスに追加する必要があります。最後に、この列にフィルタを含めるようにクエリを変更することによって、特定の値(たとえば3)、SQLが処理する必要がある行の数が10(または25 「ランダムカテゴリ」。

この新しいコラムがRandPreFilter呼ばれると仮定すると、我々は

CREATE [UNIQUE ?] INDEX 
ON system (id, RandPreFilter) 

のような指標を導入し

SELECT system.id 
    , system.username 
    , system.password 
    , system.followed 
    , system.isvalid 
    , follows.userid 
    , follows.systemid 
FROM system 
LEFT JOIN follows ON system.id = follows.systemid 
    AND follows.userid = 2 
WHERE system.followed=0 AND system.isvalid=1 
    AND follows.systemid IS NULL 

    AND RandPreFilter = 1 -- or other numbers, or possibly 
     -- FLOOR(1 + RAND() * 25) 
ORDER BY RAND() 
LIMIT 200 
5

クエリが遅い理由を次のようにクエリを変更することができデータベースがすべての表現を保持する必要があるということです生成された乱数値とその前のそれぞれのデータは、データベースから単一の行でも返すことができます。何ができるのかは、最初にWHERE RAND()< xを使用して候補行の数を制限することです。ここでxは、少なくとも必要なサンプル数を返す可能性のある数になるように選択します。本当のランダムサンプルを取得するには、RANDで再度注文するか、返されたデータセットでサンプリングする必要があります。

この方法を使用すると、データベースは、すべてのデータの大きな中間表現を構築することなく、ストリーミング方式でクエリを処理できます。欠点は、必要なサンプル数を100%確実に得ることができないことです。そうするまで、再度クエリを実行するか、小さなサンプルセットを使用するか、サンプルを段階的に追加する必要があります(重複を避けるようにします)あなたが必要とするサンプルの数を得るまで。

呼び出しごとに異なる結果を返すようにクエリを要求しない場合は、事前生成のランダム値列にインデックスを追加して、上記の手法と組み合わせることもできます。行を追加または削除しても、任意の数のサンプルを公平に取得できますが、同じデータに対して同じクエリを実行すると同じ結果セットが返されます。

1

データのランダム化の程度によっては、データを注文して余分な「最後に使用された」日時列を追加し、データを使用した後にこれを更新する価値があります。次に、最後に使用されたフィールド降順で並べ替える上位n行を選択します。

これを準備文で囲むと、ロジックを気にすることなく、一度に1つの(半)ランダム結果を選択できます。

また、すべての行に順次IDを与え、コード内でランダム性を生成し、必要な行だけを取り戻すことができます。問題は、注文前に完全なレコードセットが返されていることです。おそらく、少し遅れて

0

が、少なくともここでは、今後の検討のための余分なソリューションです:

SELECT minSystem.id, 
    minSystem.username, 
    minSystem.password, 
    minSystem.followed, 
    minSystem.isvalid, 
    randFollows.userid, 
    randFollows.systemid 
FROM 
(
    SELECT * 
    FROM system 
    WHERE system.followed = 0 AND system.isvalid = 1 
) as minSystem 
LEFT JOIN 
(
    SELECT * 
    FROM (
     SELECT * 
     FROM follows 
     WHERE follows.systemid IS NULL 
    ) as minFollows 
    WHERE rand() <= 200 * 1.5/(SELECT count(*) FROM follows WHERE systemid IS NULL) 
) as randFollows 
ON minSystem.id = randFollows.systemid 
LIMIT 200 

まず、我々はminSystemとminFollow一時テーブルのサイズを削減するために、システムテーブルの上に選択を行います。次に、計算された確率を使って、minFollowsテーブルからランダムな行を選択します。ここまでで、minSystemを使用してLEFT JOINへのかなりランダムなrandFollowsテーブルが作成されます。最後に、LIMIT 200を実行します。

MyISamを使用している場合は、単にテーブルサイズを取得できます。これにより、余分なサブクエリがなくなり、followsテーブルサイズが計算されます。また、テーブルサイズがあまりにも速くならない場合は、分母をハードコーディングすることもできます(しかし、手動メンテナンスはもっと必要です)。

より徹底し、について説明するために、私は上に掲載ソリューションチェックアウトしてください: MySQL: Alternatives to ORDER BY RAND()

・ホープ、このことができます(あるいは、少なくとも私はあなたがこの興味深いを見つけることができます願っていますが)!