Sorted sets and leaderboards

Redis · Essentials · 10 min

Sorted sets make leaderboards, priority queues and time-ordered indexes trivial and fast.

A sorted set (zset) is a set where every unique member has a floating-point score, kept in score order (ties broken lexicographically by member). Updates and lookups are O(log N).

  • ZADD key score member adds or updates; ZINCRBY key 10 ana adds to a score,
  • ZRANGE key 0 9 REV WITHSCORES gives the top 10 (highest first),
  • ZRANK/ZREVRANK give a member's position, ZSCORE its score,
  • ZRANGE key 100 200 BYSCORE returns members with scores in a range; use ( for exclusive bounds and -inf/+inf for open ends,
  • ZREM, ZCARD, ZCOUNT key min max, and ZREMRANGEBYSCORE to trim.

ZADD flags refine updates: NX only adds new members, XX only updates existing ones, GT/LT only update when the new score is greater/less, perfect for "best score" tables.

Since Redis 6.2, ZRANGE with BYSCORE, BYLEX, REV and LIMIT replaces the older ZRANGEBYSCORE and ZREVRANGE family.

Using timestamps as scores turns a zset into a time index: "events in the last hour" is ZRANGE key (now-3600) +inf BYSCORE, and trimming old entries is ZREMRANGEBYSCORE key -inf cutoff. ZPOPMIN and BZPOPMIN give a priority queue.

Example

redis-cli ZADD game:lb 120 ana 95 ben 150 cy     # (integer) 3
redis-cli ZINCRBY game:lb 40 ben                 # "135"
redis-cli ZRANGE game:lb 0 1 REV WITHSCORES
# 1) "cy"  2) "150"  3) "ben"  4) "135"
redis-cli ZREVRANK game:lb ana                   # (integer) 2   (0-based)
redis-cli ZADD game:lb GT 100 cy                 # (integer) 0   lower score ignored
redis-cli ZSCORE game:lb cy                      # "150"
redis-cli ZRANGE game:lb 100 140 BYSCORE         # 1) "ana" 2) "ben"
redis-cli ZCOUNT game:lb '(120' +inf             # (integer) 2

Remember

  • Members are unique and ordered by score; operations are O(log N)
  • ZRANGE ... REV WITHSCORES for top-N; ZREVRANK for a member's rank
  • BYSCORE with ( for exclusive bounds and -inf/+inf
  • Timestamps as scores make a time-ordered index

Watch out for

  • Expecting ZRANK to be 1-based: ranks start at 0.

Check yourself

  1. Which ZADD flag updates a player's score only if the new score is higher?
  2. Scores are timestamps. How do you fetch events from the last 60 seconds if now is 1000?

Sign in to answer, see the explanations, and get this lesson back as a quick review later.

Official docs: redis.io/docs/latest/develop/data-types/sorted-sets/