基本的な紹介
Redis 順序付きセットも、セットと同様に文字列型要素のコレクションであり、重複するメンバーは許可されません。
違いは、各要素が double 型のスコアに関連付けられていることです。 Redis はスコアを使用して、コレクションのメンバーを小さいものから大きいものまで並べ替えます。
順序付きセットのメンバーは一意ですが、スコアは繰り返すことができます。
セットはハッシュ テーブルを通じて実装されるため、追加、削除、検索の複雑さは O (1) です。コレクション内のメンバーの最大数は 2^32 - 1^ (4294967295、各コレクションには 40 億を超えるメンバーを保存できます) です。
順序付きセットは、まずセットであり、そのメンバーは一意です。次に、各メンバーがスコアに関連付けられているため、スコアに従ってメンバーを並べ替えることができます。
要件の説明
ゲーム内に何百万ものプレイヤー データがあると想像してください。プレイヤーの経験値に基づいて上位 10 位のランキングを作成する必要があるとします。 、 あなたならどうしますか?一般的なアプローチは、次のような SQL ステートメントを作成して取得します。
select * from game_socre order by score desc limit 0,20
この方法は、データ量が少ない場合には実行可能ですが、データ量が多い場合はクエリ速度が遅くなります。特に結合テーブルクエリが必要な場合、速度の低下はさらに顕著になります。
実装
現時点では、redis を使用してこの関数を実装することを検討できます。
この関数の実装に主に使用される redis データ型は、redis 順序付けセット zset です。 zset は set 型の拡張であり、元の型よりも 1 つ多いシーケンス属性を持ちます。この属性は、値が特定の順序で継続的に配置されるように、データが挿入されるたびに順序値を自動的に調整します。
主な実装アイデアは次のとおりです:
1. 新しいプレーヤーがゲームに参加すると、redis の zset に新しいレコードを追加します (レコードの内容は特定の要件によって異なります)。スコアは0
2です。プレイヤーの経験値が変化した場合、プレイヤーのスコア値を変更します
3。redisのZREVRANGEメソッドを使用してランキングを取得します
順番に戻ります設定されたキーには、指定された範囲内のメンバーが含まれます。メンバーの位置は、スコア値が大きい順(大きい順)に並べられている。同じスコア値を持つメンバーは辞書編集順にソートされます。 ZREVRANGE コマンドは、メンバーがスコア値の降順に並べられることを除いて、ZRANGE コマンドと同じです。
redis 127.0.0.1:6379> ZADD KEY_NAME SCORE1 VALUE1.. SCOREN VALUEN
1. データ準備
2. スコアトップ10のランキングを取得(ZREVRANGEは降順、ZRANGEは昇順)
3. ユーザー ee の実際のランキング (ZREVRANK は降順、ZRANK は昇順)、リアルタイム スコアを表示します
その他の要件
最新の 24 時間のユーザー ポイント ランキングを実装し、上位 10 人のプレーヤーとポイントをカウントする必要があります
実装
主な実装アイデア Yes:
ZADD を使用してユーザーの時間ごとのポイント情報を追加し、ZUNIONSTORE ユニオンを使用して 24 時間のゲーム ポイントの合計を達成することで「24 時間ランキング」を達成します。 ; (もっと良いアイデアがある場合は、以下にメッセージを残してアドバイスをいただければ幸いです)
ZUNIONSTORE destination numkeys key [key ...]
Redis Zunionstore コマンドは、1 つ以上の指定された順序セットの和集合を計算します。指定されたキーの数は numkeys パラメータで指定する必要があり、結合 (結果セット) は destination に保存されます。
デフォルトでは、結果セット内のメンバーのスコアは、指定されたすべてのセット内のそのメンバーのスコアの合計です。
考えられる問題
1. 同じスコアに関する問題
Redis は同じスコアに遭遇すると、セット メンバー自体の辞書の順序に従います。 . 並べ替えですが、ここでは「user2」と「user3」という 2 つの文字列に従って並べ替えています。逆順に並べ替えると、当然 user3 が最初にランクされます。この問題を解決するには、スコアにタイムスタンプを追加することを検討します。計算式は次のとおりです:
タイムスタンプ付きスコア = 実際のスコア*10000000000 (9999999999 – タイムスタンプ)
タイムスタンプ システムを使用します。提供される time() 関数、つまり 1970 年 1 月 1 日からの秒数では、32 ビットのタイムスタンプが 10 桁の 10 進整数 (最大値は 4294967295 )、タイムスタンプを下位 10 ビット (10 進整数) に占有させ、実際のスコアを 10^10 倍に拡張し、2 つの部分を加算した結果を zset のスコアとして使用します。逆の時系列で並べ替えていることを考慮すると、タイムスタンプの部分を逆にする必要があるため、9999999999 からタイムスタンプを減算します。プレーヤーの実際のスコアを読み取りたい場合は、最後の 10 桁を削除するだけです。
一見、この計画は良さそうに見えますが、問題が 2 つあります。
最初の問題は軽微なものです。タイムスタンプとして秒を使用すると、十分に区別できない可能性があります。同じスコアを持つ 2 つのタイムスタンプが同じ秒に出現した場合、前述の問題が引き続き発生します。もちろん、タイムスタンプを選択することもできますしかし、実際のシナリオでは、同じ秒間に誰が先頭にいるかは問題ではありません。
2 番目の問題は大きな問題で、Redis の小数型は double を使用しており、64 ビット倍精度浮動小数点数の有効桁数は 52 桁しかないため、正確に表現できる整数の範囲は - 2 です。 ^53 から 2 ^53。最大 16 個の 10 進整数のみを表現できます (最大値は 9007199254740992 ですが、実際には 16 桁でも完全には表現できません)。これは、前のタイムスタンプが 10 桁を占める場合、スコアには 6 桁しかなく、一部のリーダーボード スコアには不十分であることを意味します。たとえば 2015 年 1 月 1 日から開始するなど、タイムスタンプの桁数を減らすことも検討できますが、それでも数桁は追加されません。または、区別を減らして、タイムスタンプの単位として分と時間を使用します。
Redis のスコアタイプが int64 であれば、上記の問題は発生しません。そういえば、実際には、Redis は追加の int64 型 ZSet を提供する必要がありますが、現時点では、ソース コードを変更しない限り、それは空想に過ぎません。
PHP 関連の知識の詳細については、PHP 中国語 Web サイト をご覧ください。
以上がPHP+Redis の順序付けされた収集により、24 時間のランキングのリアルタイム更新を実現の詳細内容です。詳細については、PHP 中国語 Web サイトの他の関連記事を参照してください。