kenjuの日記

About Programming, Mathematics and Security

RedisのSortedSet型でランキング機能を実装する設計メモ

tl;dr

  • Redisのデータ型の一つであるSorted Setを利用するとランキングのような要件が気軽に実装できる
  • その場合、key/value構成は、今後の拡張性やパフォーマンスを考慮した設計を初期の段階で十分に検討すべき
  • SortedSet便利

Redisのデータ型 Sorted Sets について

名前の通り、順序が保証されたSet型(重複キーが存在しない)のこと。Redisにおける特徴は以下の通り:

  • スコアは浮動小数
  • スコアが同一の場合、辞書順に並ぶ(そして、Setsのため同一keyが存在することはありえないため、かならず順序が保証されるということになる)
  • データは昇順で格納されているが、降順で取得できるコマンド ZREVRANGEBYSCORE が存在するのでアプリケーション側で考慮する必要はない

利用イメージ

例えば、

  • User model
  • Entry model

が存在していて、それぞれのEntryに紐づくスコアを設定したいとすると、以下のようなkey/value構成が自然:

key value score
user_id entry_id raking score

具体例は以下のイメージ:

key value score
user_01 entry_01 15
user_01 entry_02 10
user_02 entry_01 10

コマンド例

使えそうなコマンド

  • ZADD : 新しいユーザー/エントリーを追加する時
  • ZINCRBY : エントリーのスコアを変更する時(負の値も設定可能のため、DECRYMENTも可能)
  • ZREVRANGEBYSCORE : スコア降順で指定した範囲のデータを取得(LIMIT/OFFSETも設定可能のため、上位3件といった取得ができる)
127.0.0.1:6379> zadd userid01 10 postid01
(integer) 1
127.0.0.1:6379> zadd userid01 10 postid02
(integer) 1
127.0.0.1:6379> zadd userid02 5 postid01
(integer) 1
127.0.0.1:6379> zrevrangebyscore userid01 +inf -inf
1) "postid02"
2) "postid01"
127.0.0.1:6379> zrevrangebyscore userid01 +inf -inf withscores
1) "postid02"
2) "10"
3) "postid01"
4) "10"
127.0.0.1:6379> ZINCRBY userid01 5 postid01
"15"
127.0.0.1:6379> zrevrangebyscore userid01 +inf -inf withscores
1) "postid01"
2) "15"
3) "postid02"
4) "10"
127.0.0.1:6379> ZINCRBY userid01 -10 postid01
"5"
127.0.0.1:6379> zrevrangebyscore userid01 +inf -inf withscores
1) "postid02"
2) "10"
3) "postid01"
4) "5"
127.0.0.1:6379>

Ruby実装

# Add
@sorted_set = Redis::SortedSet.new('user_id_01')
@sorted_set['post_id_01']  = 15
@sorted_set['post_id_02']  = 10
@sorted_set['post_id_03']  = 22

# Retrieve
@sorted_set.revrange(0,2) # => ["post_id_03", "post_id_01", "post_id_02"]

注意点

  • Sorted SetsはTTLを設定できないため、明示的にDELコマンドで削除する必要がある
  • ZUNIONSTOREを利用する場合、上限があるっぽい?のでその場合は要調査
  • Sorted Setsにはスコアを一つしか設定できないため、将来的に複数のランキングロジックを採用したい場合、工夫する必要がある
    • Redisのデータストアは1つ
      • ランキングロジックを切り替えたいタイミングで、一斉にスコアを変更する
      • key を工夫する
    • Redisのデータストアを複数持つ