4

私のサイトのユーザーは、ラップの歌詞の注釈を作成します ()。最も多くの注釈を作成した人に報酬を与えるリーダーボードを作成したいと考えています。

リーダーボードでは、各ユーザーが全体で作成した注釈の数と、過去 1 週間、1 日などに作成した注釈の数を追跡する必要があります。

全体的なリーダーボードの実装に問題はありません:

@users = User.all

<table>
  <tr>
    <th>Contributor</th>
    <th>Annotations</th>
  </tr>
    <% @users.sort_by{|u| u.annotations.size }.reverse.each do |u| %>
      <tr>
        <td><%= u %></td>
        <td><%= u.annotations.size %></td>
      </tr>
    <% end %>
</table>

しかし、毎日のスコアボードを (たとえば) 実装しようとすると、コードが繰り返され、操作が非常に遅くなります (データベースの並べ替え/カウントに依存するのではなく、メモリ内のすべての注釈を反復処理する必要があるため)。

<table>
  <tr>
    <th>Contributor</th>
    <th>Annotations</th>
  </tr>
    <% @users.sort_by{|u| u.annotations.select{|a| a.created_at > 1.day.ago }.size }.reverse.each do |u| %>
      <tr>
        <td><%= u %></td>
        <td><%= u.annotations.select{|a| a.created_at > 1.day.ago }.size %></td>
      </tr>
    <% end %>
</table>

毎日/毎週のスコアボードを実装する最良の方法は何ですか?

4

4 に答える 4

12

リーダーボードは全体として実装するのが面倒です。私の経験では、実際の実装は非常に簡単で、スケーリングが難しいだけです。多くの場合、DB をかなり集中的に使用する多くの DB クエリを実行しなければならないことがあります。日次/週次レポートを処理するには、日時列に対してクエリを実行する可能性がありますが、その列にインデックスがあることを意味します。そのインデックスは実際にはリーダーボード クエリにのみ有用であり、インデックスを再計算する必要があるため、そのテーブルに対する他のすべての書き込みに代償が払われます。

もう 1 つの方法は、スケジュールされた間隔で統計を生成し、そのデータをリーダーボード クエリで使用される別のテーブルに書き込むことです。たとえば、クエリを実行する毎晩実行されるバックグラウンド ジョブがあるとします (おそらく、datetime インデックスを使用しないためコストが高くなりますが、実行は 1 回のみであり、バックグラウンド ジョブを介した費用は「OK」です)。次に、datetime 列にインデックスある統計テーブルに書き込み、事前に計算された統計にヒットするようにリーダーボード ページを書き直します。必要に応じて、その cron スクリプトで他のデータ変更や事前計算も行うことができるため、リーダーボード ページで必要な計算を最小限に抑えることができます。

この時点で、リーダーボード ページが機能しており、インデックスを持つテーブルにヒットしている間、まだ多数の行を読み取る必要があります。これは、適切なトラフィックがあることを前提としています。インデックス付きクエリがすべてのページで多数の行にヒットすることは、依然としてコストがかかります。ページ キャッシングの実装、おそらくデータを memcached に保存することを検討します。つまり、毎日のリーダーボード データは少なくとも毎日変更されるため、定義上、すべてのページ ビューでこれらの DB クエリを再実行するにはコストがかかります。毎日のデータを memcached にキャッシュし、各ページ ビューは memcached にのみヒットする方が理にかなっています。

ご覧のとおり、進化したプロセスです。トラフィックが少ない場合は、個別のテーブルを持たず、datetime 列にインデックスを付けるだけで済む可能性があります。実行中の合計、カウント、および平均は問題ない場合があります。しかし、それはスケーリングしません。そのため、より最適化された構造に分割することを検討する必要があります。そして、基礎となるデータが 24 時間以内に変更されない間に同じクエリを毎日何度も実行するとコストがかかることがわかり、キャッシュ設定に移行します。多くの可動部分があり、複雑になる可能性があります。

リーダーボードはゲームの仕組みや人々のやる気を引き出すのに最適ですが (誰もがスコアを見るのが大好きです!)、大規模な作業を行うのは大変です。

于 2010-01-02T20:15:37.710 に答える
3

これらの統計を、オブザーバーによって更新される別のテーブル/モデルに保持することを検討しましたか? ここでは、ビューで多くの重い作業を行っていますが、これは一般的に良い習慣ではありません。

于 2010-01-02T20:16:32.123 に答える
3

Redis の使用に関する Jeff の提案に加えて、redis に便乗してリーダーボードの作業に使用している ruby​​ gem を次に示します: https://github.com/agoragames/leaderboard

于 2012-05-11T15:18:44.110 に答える
3

Redis を使用することをお勧めします。DB からデータを取得し、それを Redis のソート済みセットに入れる cron タイプのタスクを実行できます。ソートされたセット機能は、おそらくリーダーボードを保存するための最良のユーティリティです。 http://redis.io/topics/data-types

于 2011-06-17T06:53:51.573 に答える