目次
同一のポイントは考慮されません
同じポイントは時間順にソートされ、ランキングは一意になります
設計2
同ポイントを時間順に並べた同順位ランキング
ホームページ データベース Redis Redis がランキングと時間による同一ポイントソート機能を実装する方法の詳細な例

Redis がランキングと時間による同一ポイントソート機能を実装する方法の詳細な例

Aug 26, 2022 pm 02:09 PM
redis

推奨学習: Redis ビデオ チュートリアル

日々の開発では、ユーザー スコアなどを計算する必要に遭遇することがよくあります。たとえば、ゲームでは戦闘効率をランク付けする必要があり、チーム活動では各チームの貢献値をランク付けする必要があり、WeChat では各友達の歩数をランク付けする必要があります。この場合、一般に、Redis の順序が選択されます。コレクションには、ランキングのニーズを満たすためにユーザーのスコアが保存されます。ただし、シナリオごとのランキング方法も若干異なります。以下は、私の日々の開発に基づいた要約です。

要件: チーム アクティビティにおける各チームの貢献値をランク付けします。

同一のポイントは考慮されません

Redis のソート セットは、文字列型の順序付きセットです。セットのメンバーは一意であるため、セット内に重複したデータが存在することはできません。

各要素は double 型のスコアに関連付けられます。 Redis はスコアを使用して、コレクションのメンバーを小さいものから大きいものまで並べ替えます。

順序付きセットのメンバーは一意ですが、スコアは繰り返すことができます。

同じポイントを持つ状況を無視して、ランキング リストを実装します:

// 准备数据,其中value为每个队伍的ID,score为队伍的贡献值
> zadd z1 5 a 6 b 1 c 2 d 10 e
(integer) 5

// 分页查询排行榜所有的队伍和贡献值,要使用zrevrange,而不是zrange,贡献值越大越排在前面
> zrevrange z1 0 2 withscores
1) "e"
2) "10"
3) "b"
4) "6"
5) "a"
6) "5"

// 增加某个队伍的贡献值
> zincrby z1 3 d
"5"
> zincrby z1 4 c
"5"

// 查询排行榜所有的队伍
> zrevrange z1 0 -1 withscores
 1) "e"
 2) "10"
 3) "b"
 4) "6"
 5) "d"
 6) "5"
 7) "c"
 8) "5"
 9) "a"
10) "5"

// 查询某个队伍的排名
> zrevrank z1 d
(integer) 2
ログイン後にコピー

Redis のデフォルトの実装では、同じスコアを持つメンバーが辞書順 (09、AZ、上記で使用しているa~z)はzrevrangeなので逆順となり、同じスコアでのソートは時間優先でソートできません。

同じポイントは時間順にソートされ、ランキングは一意になります

上記の実装では、2 つのチームの貢献値が同じ、つまりポイント値が同じである場合、時間に基づいてランク付けすることはできません。

したがって、 スコア = 貢献値のタイムスタンプ を設計する必要があります。スコアが高い人が最初にランクされます。最後に、スコアに基づいて貢献値を分析する必要があります。

設計 1

スコア値の保存には整数を使用します。redis のスコア自体は double 型であり、正確に保存できる最大整数は 2^53=9007199254740992 (16 ビット) です。 )。ミリ秒まで正確なタイムスタンプには 13 桁が必要で、ストレージ寄与値には 3 桁のみが残ります。現在、時刻が秒まで正確な場合、必要なのは 10 桁のみで、寄与値には 6 桁が残ります。

全体的な設計: 上位 3 ビットは寄与値を表し、下位 13 ビットはタイムスタンプを表します。

単純にスコア構造をまとめると、貢献値 * 10^13 タイムスタンプ になります。スコアが大きいほどスコアは近くなり、タイムスタンプが小さいほどスコアは近くなります。このように、2つの判定ルールは逆のものもあり、単純に両者を組み合わせてスコア化することはできません。

しかし、逆に考えて、同じ十分に大きな数値 Integer.MAX からタイムスタンプを減算することもできます。タイムスタンプが小さいほど、得られる差が大きくなるため、スコアの構造を変更できます。値に貢献します。 to: * 10^13 (Integer.MAX-timestamp) これにより、ニーズを満たすことができます。

設計2

redisのスコア値はdouble型なので、整数部分に貢献値を格納し、小数部分にタイムスタンプを格納し、最大値を使用することができます。同じタイムスタンプの部分からそれを減算します。

このようにして、全体的な設計は次のようになります: スコア = 貢献値 (Integer.MAX-タイムスタンプ) * 10^-13

欠点: スコア値は変数を使用して計算されるため、チームに貢献値を追加する場合、以前のzincrbyを使用してスコア値を変更することはできず、同時条件でチームに貢献値を追加すると、スコアが不正確になります。価値観。

エラー状況シミュレーション:

チーム A の現在の貢献値が 10 であるとします。チーム A のプレイヤー X がチームに貢献値を 1 追加し、プログラムでスコアが計算されます。チーム A のプレーヤー Y はチームに貢献値 1 を追加し、スコアはプログラムで 11 になるように計算されます。yyy チーム A のプレーヤー X が redis の zadd コマンドを呼び出して、チームの貢献値を 11 に設定します。 .xxx チーム A のプレイヤー Y が redis の zadd コマンドを呼び出します。 チームの貢献値を 11.yyy に設定します。 最終的に、チーム A の貢献値は 11 と計算されます。貢献値を増やす操作のアトミック性は保証できません。

現時点では、lua スクリプトを使用して、貢献値の計算と設定の 2 つの操作のアトミック性を確保する必要があります。

// 其中KEYS[1]为排行榜key,KEYS[2]为队伍ID
// 其中ARGV[1]为增加的贡献值,ARGV[2]为Integer.MAX-时间戳
local score = redis.call('zscore', KEYS[1], KEYS[2]) 
if not(score) then
	score=0 
end 
score=math.floor(score) + tonumber(ARGV[1]) + tonumber(ARGV[2]) 
redis.call('zadd', KEYS[1], score, KEYS[2]) return 1
ログイン後にコピー

redis では time 関数を使用できないため、 (Integer.MAX - タイムスタンプ) * 10^-13 の部分は、スクリプトの外のプログラムによって計算されて渡されます。

ランキング リストのページングやチームのランキングのクエリなどの機能には、引き続き上記のコマンドを使用できます。

同ポイントを時間順に並べた同順位ランキング

いわゆる同順位ランキングとは、順位状況が同じ順位のランキングです。

期待される結果は次のとおりです:

#b992c992##de

当然现实中也有排名不跳过的情况,我这里考虑的是排名跳过的情况。

redis中score的设计还是采用上面的分数=贡献值 + (Integer.MAX-时间戳) * 10^-13,只是在查询排名时需要进行计算。

比如要查上表中队伍b的排名,思路如下:

  • 首先查到队伍b的score
  • 再查到跟队伍b的score的整数部分相同(也就是贡献值一样),排在第一个的队伍的value(队伍ID)
  • 根据上一步得到的队伍ID查询此队伍的排名就是队伍b的排名

使用命令实现上面的步骤如下:

> zscore 排行榜key teamId
> zrevrangebyscore(排行榜key, 上一步得到的score+1, 上一步得到的score, limit, 0 , 1)
> zrevrank(排行榜key, 上一步得到的teamId)
ログイン後にコピー

为了性能考虑,可以使用下面的脚本一次查出来:

// KEYS[1]表示排行榜key
// KEYS[2]表示要查询的队伍的ID
local rank = 0 
local score = redis.call('zscore', KEYS[1], KEYS[2]) 
if not(score) then
    score=0 
else 
    score=math.floor(score) 
    local firstScore = redis.call('zrevrangebyscore', KEYS[1], score+1, score, 'limit', 0, 1) 
    rank=redis.call('zrevrank', KEYS[1], firstScore[1]) 
end 
return {score,rank}
ログイン後にコピー

下面附上分页查询排行榜的脚本,假如一页10条,不用下面的脚本需要查询10次上面的脚本,如果连上面的脚本都没有使用的话就要查询30次redis。

// 排行榜key
// ARGV[1]分页起始偏移
// ARGV[2]分页结束偏移
local list = redis.call('zrevrange', KEYS[1], ARGV[1], ARGV[2], 'withscores') 
local result={} 
local i = 1 
for k,v in pairs(list) do 
    if k%2 == 0 then 
        local teamId = list[k-1] 
        local score = math.floor(v) 
        local firstScore = redis.call('zrevrangebyscore', KEYS[1], score+1, score, 'limit', 0, 1) 
        local rank=redis.call('zrevrank', KEYS[1], firstScore[1]) 
        local l = {teamId=teamId, contributionValue=score, teamRank=rank+1} 
        result[i] = l i = i + 1 
    end 
end 
return cjson.encode(result)
ログイン後にコピー

此脚本使用了cjson库,返回的是一个json。

推荐学习:Redis视频教程

以上がRedis がランキングと時間による同一ポイントソート機能を実装する方法の詳細な例の詳細内容です。詳細については、PHP 中国語 Web サイトの他の関連記事を参照してください。

このウェブサイトの声明
この記事の内容はネチズンが自主的に寄稿したものであり、著作権は原著者に帰属します。このサイトは、それに相当する法的責任を負いません。盗作または侵害の疑いのあるコンテンツを見つけた場合は、admin@php.cn までご連絡ください。

ホットAIツール

Undresser.AI Undress

Undresser.AI Undress

リアルなヌード写真を作成する AI 搭載アプリ

AI Clothes Remover

AI Clothes Remover

写真から衣服を削除するオンライン AI ツール。

Undress AI Tool

Undress AI Tool

脱衣画像を無料で

Clothoff.io

Clothoff.io

AI衣類リムーバー

Video Face Swap

Video Face Swap

完全無料の AI 顔交換ツールを使用して、あらゆるビデオの顔を簡単に交換できます。

ホットツール

メモ帳++7.3.1

メモ帳++7.3.1

使いやすく無料のコードエディター

SublimeText3 中国語版

SublimeText3 中国語版

中国語版、とても使いやすい

ゼンドスタジオ 13.0.1

ゼンドスタジオ 13.0.1

強力な PHP 統合開発環境

ドリームウィーバー CS6

ドリームウィーバー CS6

ビジュアル Web 開発ツール

SublimeText3 Mac版

SublimeText3 Mac版

神レベルのコード編集ソフト(SublimeText3)

Redisクラスターモードの構築方法 Redisクラスターモードの構築方法 Apr 10, 2025 pm 10:15 PM

Redisクラスターモードは、シャードを介してRedisインスタンスを複数のサーバーに展開し、スケーラビリティと可用性を向上させます。構造の手順は次のとおりです。異なるポートで奇妙なRedisインスタンスを作成します。 3つのセンチネルインスタンスを作成し、Redisインスタンスを監視し、フェールオーバーを監視します。 Sentinel構成ファイルを構成し、Redisインスタンス情報とフェールオーバー設定の監視を追加します。 Redisインスタンス構成ファイルを構成し、クラスターモードを有効にし、クラスター情報ファイルパスを指定します。各Redisインスタンスの情報を含むnodes.confファイルを作成します。クラスターを起動し、CREATEコマンドを実行してクラスターを作成し、レプリカの数を指定します。クラスターにログインしてクラスター情報コマンドを実行して、クラスターステータスを確認します。作る

Redisデータをクリアする方法 Redisデータをクリアする方法 Apr 10, 2025 pm 10:06 PM

Redisデータをクリアする方法:Flushallコマンドを使用して、すべての重要な値をクリアします。 FlushDBコマンドを使用して、現在選択されているデータベースのキー値をクリアします。 [選択]を使用してデータベースを切り替え、FlushDBを使用して複数のデータベースをクリアします。 DELコマンドを使用して、特定のキーを削除します。 Redis-CLIツールを使用してデータをクリアします。

Redisキューの読み方 Redisキューの読み方 Apr 10, 2025 pm 10:12 PM

Redisのキューを読むには、キュー名を取得し、LPOPコマンドを使用して要素を読み、空のキューを処理する必要があります。特定の手順は次のとおりです。キュー名を取得します:「キュー:キュー」などの「キュー:」のプレフィックスで名前を付けます。 LPOPコマンドを使用します。キューのヘッドから要素を排出し、LPOP Queue:My-Queueなどの値を返します。空のキューの処理:キューが空の場合、LPOPはnilを返し、要素を読む前にキューが存在するかどうかを確認できます。

Redisコマンドの使用方法 Redisコマンドの使用方法 Apr 10, 2025 pm 08:45 PM

Redis指令を使用するには、次の手順が必要です。Redisクライアントを開きます。コマンド(動詞キー値)を入力します。必要なパラメーターを提供します(指示ごとに異なります)。 Enterを押してコマンドを実行します。 Redisは、操作の結果を示す応答を返します(通常はOKまたは-ERR)。

Redisロックの使用方法 Redisロックの使用方法 Apr 10, 2025 pm 08:39 PM

Redisを使用して操作をロックするには、setnxコマンドを介してロックを取得し、有効期限を設定するために有効期限コマンドを使用する必要があります。特定の手順は次のとおりです。(1)SETNXコマンドを使用して、キー価値ペアを設定しようとします。 (2)expireコマンドを使用して、ロックの有効期限を設定します。 (3)Delコマンドを使用して、ロックが不要になったときにロックを削除します。

Redisのソースコードを読み取る方法 Redisのソースコードを読み取る方法 Apr 10, 2025 pm 08:27 PM

Redisソースコードを理解する最良の方法は、段階的に進むことです。Redisの基本に精通してください。開始点として特定のモジュールまたは機能を選択します。モジュールまたは機能のエントリポイントから始めて、行ごとにコードを表示します。関数コールチェーンを介してコードを表示します。 Redisが使用する基礎となるデータ構造に精通してください。 Redisが使用するアルゴリズムを特定します。

Redisコマンドラインの使用方法 Redisコマンドラインの使用方法 Apr 10, 2025 pm 10:18 PM

Redisコマンドラインツール(Redis-Cli)を使用して、次の手順を使用してRedisを管理および操作します。サーバーに接続し、アドレスとポートを指定します。コマンド名とパラメーターを使用して、コマンドをサーバーに送信します。ヘルプコマンドを使用して、特定のコマンドのヘルプ情報を表示します。 QUITコマンドを使用して、コマンドラインツールを終了します。

Centos RedisでLUAスクリプト実行時間を構成する方法 Centos RedisでLUAスクリプト実行時間を構成する方法 Apr 14, 2025 pm 02:12 PM

Centosシステムでは、Redis構成ファイルを変更するか、Redisコマンドを使用して悪意のあるスクリプトがあまりにも多くのリソースを消費しないようにすることにより、LUAスクリプトの実行時間を制限できます。方法1:Redis構成ファイルを変更し、Redis構成ファイルを見つけます:Redis構成ファイルは通常/etc/redis/redis.confにあります。構成ファイルの編集:テキストエディター(VIやNANOなど)を使用して構成ファイルを開きます:sudovi/etc/redis/redis.conf luaスクリプト実行時間制限を設定します。

See all articles
チーム ID貢献度ランキング
a1001
884
875