はじめに
とあるサービスでランキング計算をする実装を組んでいた。ふと、アプリケーション側ではなく、Redisに任せることもできることを思い出したので、調査がてらサンプルコードを書いたので記事にまとめる。
実現したいこと
- スコア情報を持つデータがある。Redisを使用して同点順位によるランキングを作成したい
開発環境
- Redis 4.0.11
- Ruby 2.6.1p33
- gem
- redis 4.1.2
実装方法
今回は動作検証のためRubyと redis gemを使用して実装をした。後ほどサンプルコード全文を共有する。
準備
まずは redis gemを利用してRedisに接続をする。Redisに接続したら、以降は REDIS
オブジェクトを通じてRedisを操作する。
require 'redis'
# Redisに接続
REDIS = Redis.new(host: 'localhost', port: 6379)
# 初期化
REDIS.del("ranking")
RedisのSorted Setで実装
日本語ではソート済みセット型と呼ばれ、Redisにスコアを登録するとRedis側で並び替えをしてくれる。
新しいデータを登録するには zadd を使用する。データは以下のようにして追加する。これを追加するだけでRedis側が自動的に並び替えをしてくれる。
REDIS.zadd("ranking", 200, "A")
ソート済みのデータを取得するには zrevrangeを使用する。降順のランキング情報を1件目から10件スコア情報も合わせて取得できる。 :with_scores => true
を指定することでスコア情報も合わせて返すこともできる。
rankings = REDIS.zrevrange("ranking", 0, 10, :with_scores => true)
順位計算は zcountを使用する。このメソッドは指定の範囲でスコアが何件あるかを計算する。下記の例だと指定のスコア以上の値が何件あるかを算出している。これにより同点順位が計算できる。
rankings.each do |r|
# 同点順位計算処理: 同じスコア以上のものが何件あるか算出する
rank = REDIS.zcount("ranking", r[1], "+inf")
p r
p rank
end
サンプルコード
今回のサンプルコードはこちら。
require 'redis'
# Redisに接続
REDIS = Redis.new(host: 'localhost', port: 6379)
# 初期化
REDIS.del("ranking")
# 検証データを登録
REDIS.zadd("ranking", 200, "A")
REDIS.zadd("ranking", 150, "B")
REDIS.zadd("ranking", 30, "C")
REDIS.zadd("ranking", 200, "D")
REDIS.zadd("ranking", 180, "E")
REDIS.zadd("ranking", 190, "F")
REDIS.zadd("ranking", 250, "G")
REDIS.zadd("ranking", 210, "H")
REDIS.zadd("ranking", 10, "I")
REDIS.zadd("ranking", 30, "J")
# 降順のランキング情報を1件目から10件スコア情報も合わせて取得す
rankings = REDIS.zrevrange("ranking", 0, 10, :with_scores => true)
rankings.each do |r|
# 同点順位計算処理: 同じスコア以上のものが何件あるか算出する
rank = REDIS.zcount("ranking", r[1], "+inf")
p r
p rank
end
実行結果
実行結果を見る。ポイントは4位。200点で同点が2つあるため、4位が2つとなっており、190点が5位から始まっている。これで実現したいことは達成できた。
$ bundle exec ruby ranking.rb
["G", 250.0]
1
["H", 210.0]
2
["D", 200.0]
4
["A", 200.0]
4
["F", 190.0]
5
["E", 180.0]
6
["B", 150.0]
7
["J", 30.0]
9
["C", 30.0]
9
["I", 10.0]
10
終わりに
あるあるな実装例ではあるが、Redisの Sorted Setを利用したことが無かったので、学習までにサンプルコードを書いて記事にまとめた。アプリケーション側で順位計算ができているなら、Redisにさせる必要もない。ただゲームなどでリアルタイム性が必要な要件がある場合は、Redisに任せるのが良さそうである。