Redis Sorted Sets(パート2)

このレッスンでは、スコア範囲クエリや集合演算などの高度なソート済みセット機能について解説します。

ZRANGEBYSCORE:スコア範囲によるクエリ

ZRANGEBYSCOREはスコア範囲に基づいて要素を取得します。

基本的な使用方法

REDIS
ZADD leaderboard 100 "player1" 150 "player2" 200 "player3" 250 "player4" 300 "player5"

# スコアが150〜250の要素を取得
ZRANGEBYSCORE leaderboard 150 250
1) "player2"
2) "player3"
3) "player4"

# スコア付きで要素を取得
ZRANGEBYSCORE leaderboard 150 250 WITHSCORES
1) "player2"
2) "150"
3) "player3"
4) "200"
5) "player4"
6) "250"

-inf と +inf の使用

REDIS
# スコア <= 200 の要素を取得
ZRANGEBYSCORE leaderboard -inf 200
1) "player1"
2) "player2"
3) "player3"

# スコア >= 200 の要素を取得
ZRANGEBYSCORE leaderboard 200 +inf
1) "player3"
2) "player4"
3) "player5"

# すべての要素を取得
ZRANGEBYSCORE leaderboard -inf +inf

開区間と閉区間

REDIS
# デフォルトは閉区間 [150, 250]
ZRANGEBYSCORE leaderboard 150 250

# 開区間 (150, 250) — 150と250を除外
ZRANGEBYSCORE leaderboard (150 (250

# 半開区間 [150, 250)
ZRANGEBYSCORE leaderboard 150 (250

# 半開区間 (150, 250]
ZRANGEBYSCORE leaderboard (150 250
💡 構文: 開区間(排他的)を示すには括弧 ( を使用し、閉区間(包括的)を示すには括弧なしにします。

LIMITによるページネーション

REDIS
# スコア100〜300の要素を、インデックス1から2つ取得
ZRANGEBYSCORE leaderboard 100 300 LIMIT 1 2
1) "player2"
2) "player3"

ZREVRANGEBYSCORE:スコア範囲によるクエリ(高い方から)

REDIS
# スコア150〜250の要素を、高い方から取得
ZREVRANGEBYSCORE leaderboard 250 150 WITHSCORES
1) "player4"
2) "250"
3) "player3"
4) "200"
5) "player2"
6) "150"
⚠️ 補足: ZREVRANGEBYSCOREのスコア範囲は高い方から低い方へ(250 150)です。低い方から高い方ではありません。

ZRANGEBYLEX:辞書順によるクエリ

すべての要素が同じスコアを持つ場合、辞書順(アルファベット順)でクエリできます。

基本的な使用方法

REDIS
# すべての要素がスコア0
ZADD myzset 0 "apple" 0 "banana" 0 "cherry" 0 "date"

# 辞書順ですべての要素を取得
ZRANGEBYLEX myzset - +
1) "apple"
2) "banana"
3) "cherry"
4) "date"

# 辞書順の範囲 [banana, cherry] の要素を取得
ZRANGEBYLEX myzset [banana [cherry
1) "banana"
2) "cherry"

# "b"で始まる要素を取得
ZRANGEBYLEX myzset [b (c
1) "banana"
💡 使用例: ZRANGEBYLEXはオートコンプリートや辞書検索の実装に最適です。

ZREMRANGEBYRANK:ランク範囲で削除

REDIS
ZADD leaderboard 100 "p1" 150 "p2" 200 "p3" 250 "p4" 300 "p5"

# ランク0〜1の要素を削除(最も低い2つのスコア)
ZREMRANGEBYRANK leaderboard 0 1
(integer) 2

ZRANGE leaderboard 0 -1
1) "p3"
2) "p4"
3) "p5"

ZREMRANGEBYSCORE:スコア範囲で削除

REDIS
ZADD leaderboard 100 "p1" 150 "p2" 200 "p3" 250 "p4" 300 "p5"

# スコア100〜200の要素を削除
ZREMRANGEBYSCORE leaderboard 100 200
(integer) 3

ZRANGE leaderboard 0 -1 WITHSCORES
1) "p4"
2) "250"
3) "p5"
4) "300"

使用例:期限切れデータのクリーンアップ

REDIS
# 遅延キュー(スコアはタイムスタンプ)
ZADD delay:queue 1719129600 "task:1"
ZADD delay:queue 1719133200 "task:2"

# 完了したタスクを削除(現在時刻より前のタイムスタンプ)
ZREMRANGEBYSCORE delay:queue -inf 1719129600

ZREMRANGEBYLEX:辞書順範囲で削除

REDIS
ZADD myzset 0 "apple" 0 "banana" 0 "cherry" 0 "date"

# 辞書順の範囲 [banana, cherry] の要素を削除
ZREMRANGEBYLEX myzset [banana [cherry
(integer) 2

ZRANGE myzset 0 -1
1) "apple"
2) "date"

Sorted Setの集合演算

ZUNIONSTORE:和集合

REDIS
# 2つのソート済みセットを作成
ZADD zset1 1 "a" 2 "b" 3 "c"
ZADD zset2 4 "b" 5 "c" 6 "d"

# 和集合を計算(デフォルト:スコアの合計)
ZUNIONSTORE result 2 zset1 zset2
(integer) 4

ZRANGE result 0 -1 WITHSCORES
1) "a"
2) "1"
3) "d"
4) "6"
5) "b"
6) "6"   # 2 + 4
7) "c"
8) "8"   # 3 + 5

ZUNIONSTORE with 重み

REDIS
# 重みを設定
ZUNIONSTORE result 2 zset1 zset2 WEIGHTS 2 3
# zset1のスコアは2倍、zset2のスコアは3倍

ZRANGE result 0 -1 WITHSCORES
1) "a"
2) "2"   # 1 * 2
3) "d"
4) "18"  # 6 * 3
5) "b"
6) "16"  # 2*2 + 4*3
7) "c"
8) "21"  # 3*2 + 5*3

ZUNIONSTOREの集計関数

REDIS
# MIN集計を使用(最小のスコアを採用)
ZUNIONSTORE result 2 zset1 zset2 AGGREGATE MIN

# MAX集計を使用(最大のスコアを採用)
ZUNIONSTORE result 2 zset1 zset2 AGGREGATE MAX

# SUM集計を使用(合計、デフォルト)
ZUNIONSTORE result 2 zset1 zset2 AGGREGATE SUM

ZINTERSTORE:積集合

REDIS
ZADD zset1 1 "a" 2 "b" 3 "c"
ZADD zset2 4 "b" 5 "c" 6 "d"

# 積集合を計算
ZINTERSTORE result 2 zset1 zset2
(integer) 2

ZRANGE result 0 -1 WITHSCORES
1) "b"
2) "6"   # 2 + 4
3) "c"
4) "8"   # 3 + 5

ZDIFFSTORE:差集合(Redis 6.2+)

REDIS
ZADD zset1 1 "a" 2 "b" 3 "c"
ZADD zset2 4 "b" 5 "c" 6 "d"

# 差集合を計算
ZDIFFSTORE result 2 zset1 zset2
(integer) 1

ZRANGE result 0 -1 WITHSCORES
1) "a"
2) "1"

ZMSCORE:バッチスコア取得(Redis 6.2+)

REDIS
ZADD leaderboard 100 "player1" 200 "player2" 300 "player3"

ZMSCORE leaderboard "player1" "player2" "player4"
1) "100"
2) "200"
3) (nil)  # player4は存在しない

ZPOPMAXとZPOPMIN:最高/最低スコア要素をポップ

ZPOPMAX:最高スコア要素をポップ

REDIS
ZADD leaderboard 100 "p1" 200 "p2" 300 "p3"

# 最高スコアの要素をポップ
ZPOPMAX leaderboard
1) "p3"
2) "300"

# 最高スコアの2要素をポップ
ZPOPMAX leaderboard 2
1) "p2"
2) "200"
3) "p1"
4) "100"

ZPOPMIN:最低スコア要素をポップ

REDIS
ZADD leaderboard 100 "p1" 200 "p2" 300 "p3"

ZPOPMIN leaderboard
1) "p1"
2) "100"

BZPOPMAXとBZPOPMIN:ブロッキングポップ

REDIS
# ソート済みセットが空の場合、最大10秒ブロック待機
BZPOPMAX leaderboard 10

# 複数のソート済みセットを監視
BZPOPMAX zset1 zset2 zset3 10
💡 使用例: BZPOPMAX/BZPOPMINは優先度キューの実装に使用できます。

高度なSorted Setアプリケーション

ユースケース1:スライディングウィンドウのレート制限

REDIS
# ユーザーのリクエストタイムスタンプを記録
ZADD ratelimit:user:1 1719129600 "req1"
ZADD ratelimit:user:1 1719129605 "req2"
ZADD ratelimit:user:1 1719129610 "req3"

# 過去60秒間のリクエスト数をカウント
ZCOUNT ratelimit:user:1 1719129540 1719129600

# 60秒より古いリクエストをクリーンアップ
ZREMRANGEBYSCORE ratelimit:user:1 -inf 1719129540

ユースケース2:オートコンプリート

REDIS
# すべての単語がスコア0
ZADD autocomplete 0 "apple" 0 "application" 0 "appetite" 0 "banana"

# "app"で始まる単語をクエリ
ZRANGEBYLEX autocomplete [app [app\xff
1) "apple"
2) "application"
3) "appetite"

ユースケース3:タイムライン

REDIS
# ユーザータイムライン(スコアはタイムスタンプ)
ZADD timeline:user:1 1719129600 "post:1"
ZADD timeline:user:1 1719129605 "post:2"
ZADD timeline:user:1 1719129610 "post:3"

# 最新の10件の投稿を取得
ZREVRANGE timeline:user:1 0 9

# 時間範囲内の投稿を取得
ZRANGEBYSCORE timeline:user:1 1719129600 1719129610

ユースケース4:多次元ランキング

REDIS
# 商品販売ランキング
ZADD sales:rank 1000 "product:1"
ZADD sales:rank 2000 "product:2"

# 商品評価ランキング
ZADD rating:rank 4.5 "product:1"
ZADD rating:rank 4.8 "product:2"

# 複合ランキング(販売重み0.6、評価重み0.4)
ZUNIONSTORE combined:rank 2 sales:rank rating:rank WEIGHTS 0.6 0.4

❓ よくある質問

Q ZRANGEBYSCOREとZRANGEの違いは何ですか?
A ZRANGEはランク範囲でクエリし、ZRANGEBYSCOREはスコア範囲でクエリします。
Q スコアが最も高いN個の要素を取得するにはどうすればよいですか?
A ZREVRANGE key 0 N-1 WITHSCORES を使用します。
Q ZUNIONSTOREの重みはどのように機能しますか?
A 重みは乗数です。WEIGHTS 2 3 は最初のセットのスコアを2倍、2番目のセットのスコアを3倍にします。
Q ZRANGEBYLEXを使用するには、すべての要素が同じスコアである必要がありますか?
A はい。ZRANGEBYLEXはすべての要素が同じスコアであることを前提とし、辞書順でソートします。
Q 優先度キューを実装するにはどうすればよいですか?
A ZADDでタスクを追加(スコア = 優先度)し、ZPOPMINで最も優先度の高いタスクをポップします。

📖 まとめ

📝 練習問題

  1. スコア範囲クエリ: ZRANGEBYSCOREを使用して異なるスコア範囲の要素をクエリしましょう
  2. 範囲削除: ZREMRANGEBYSCOREを使用して期限切れデータをクリーンアップしましょう
  3. 集合演算: ZUNIONSTOREを使用して異なる重みで複数のリーダーボードをマージしましょう
  4. スライディングウィンドウ: Sorted Setを使用してスライディングウィンドウのレート制限を実装しましょう

次のレッスン

次のレッスンでは、Redis HyperLogLogについて学びます。カーディナリティ推定について解説します。

100%