Redis vs Memcached for Leaderboards
A leaderboard is an ordered set of members with a numeric score, updated under load, queried by rank. Redis ships exactly that data structure with ZADD and ZREVRANGE. Memcached ships a key-value blob store. The gap is not a benchmark margin, it is an architectural one.
The sorted set, the data structure Memcached does not have
Redis sorted sets (type zset) are unique-member collections where every member carries a floating-point score. The collection is kept in score order at all times. Two operations on this structure are essentially free: insert at log time, and range-by-rank at log plus result size. A third operation, rank lookup by member, is also log time. These three operations are everything you need for a leaderboard.
The implementation is a skip list (for the ordered traversal) paired with a hash table (for the by-member lookup). The skip list was chosen over a balanced binary tree because the skip list code is simpler, cache-friendlier, and easier to make lock-free for the future threaded Redis paths. The hash table is the standard Redis hashtable: open addressing, incremental rehashing during background work. The combined per-entry memory cost is approximately 80 bytes plus the member string itself, which means a million-entry leaderboard with 12-byte member strings (user IDs) lands around 100MB.
Memcached has none of this. A Memcached value is an opaque byte string with a size limit (default 1MB, configurable). To build a leaderboard you would need to store the entire member list in one giant key, fetch it on every update, sort in application code, write back. That is O(N) network bandwidth per update, O(N log N) CPU per update, racy unless you wrap the read-modify-write in CAS, and limited to whatever fits in one Memcached value. For a 100k-entry leaderboard with 50-byte members, the payload is 5MB, over the default limit and over most production limits even when raised.
The five commands you actually need
# Submit a score (only if higher than current) ZADD weekly_leaderboard GT 1850 "user:42" # Top 10 ZREVRANGE weekly_leaderboard 0 9 WITHSCORES # Player's rank (0-indexed, highest = rank 0) ZREVRANK weekly_leaderboard "user:42" # Player's score ZSCORE weekly_leaderboard "user:42" # Window around a player (rank +/- 5) LUA_SCRIPT(GET ZREVRANK r THEN ZREVRANGE r-5 r+5)
Real-world: game and product leaderboards
The mobile game industry runs on Redis sorted sets. Pokemon Go's leaderboards, Clash of Clans clan rankings, Fortnite's tournament boards, and most leaderboard-as-a-service products (PlayFab, GameSparks, Lootlocker) build on Redis primarily because the math works out. A game with ten million players submitting one score per session generates ten million ZADD ops per round, returns top-N pages in single-digit milliseconds, and supports player-centric "ranks near you" views with two commands.
Outside gaming, Redis sorted sets power Stack Overflow's reputation rankings, Reddit's hot-post scoring (score plus time-decay calculated server-side and ZADDed), Discord's voice channel speaking-order, and most "top 10 most-viewed articles this hour" sidebars across publishing. The pattern is so common that the Redis documentation lists leaderboards as the canonical sorted-set use case.
Time-based leaderboards (daily / weekly / all-time) get their own keys with TTLs. Daily leaderboard? lb:2026-05-18 with a 48-hour TTL. Aggregate the weekly by ZUNIONSTORE-ing 7 daily keys into a weekly key during a nightly job. ZUNIONSTORE accepts weights, so you can build a decay function where today counts 1.0, yesterday 0.7, and so on. Memcached cannot do unions, period.
Scaling: when one node is not enough
A single Redis primary handles roughly 100K-200K ZADD ops per second on commodity hardware (DevGenius March 2026 benchmark, AWS c6i.2xlarge). For most products that is plenty, including a million daily active users each submitting a handful of scores. When you need more, the question is how to shard a sorted set without breaking the ranking semantics that made you pick a sorted set in the first place.
Naive sharding (hash-by-member to one of N shards) breaks global ranking. To get a global top-K from N shards you fetch the top-K from each, merge them in application code, and you have read K*N items to display K. This is fine when K is small (top 10 from 16 shards is 160 items, fast) but not when the user wants to see their rank in a 50-million-player leaderboard and the answer requires scanning every shard.
The production pattern most large-scale leaderboards adopt is region or segment sharding: each region has its own leaderboard, the user only sees their region, and a separate (smaller, cached, often hourly) global view aggregates the top tier of each region. Riot Games and Blizzard both use variants of this for their competitive ladders. The structure mirrors the way humans think about rankings anyway. Memcached, as a reminder, cannot do any of this even on a single node.
FAQ
Can Memcached build a leaderboard?
Not without re-implementing sorted-set logic in the application. Memcached stores opaque blobs and has no notion of score ordering. You would have to fetch all entries, sort in application code, and write back the rank, which is O(N) per update and racy under concurrency. The fact that production leaderboards do not use Memcached is not an oversight.
How does Redis sorted set work internally?
A skip list plus a hash table. The skip list gives O(log N) insertion and O(log N + K) range queries. The hash table gives O(1) score lookup by member. Memory overhead is approximately 80 bytes per entry plus the member string. At 10 million entries that is roughly 800MB before string size.
What is ZADD GT and when do I use it?
ZADD key GT score member only updates the score if the new score is greater than the existing score. Useful for high-score leaderboards where you only want to record an improvement, not a regression. Added in Redis 6.2 and supported by Valkey.
How do I get a player's rank?
ZREVRANK key member returns the 0-indexed rank (highest score first). ZSCORE returns the member's score. Combine with ZREVRANGE to fetch the top N players. All three are O(log N).
Can I shard a leaderboard?
Yes but with caveats. Range queries across shards require fetching the top K from each shard then merging, which is O(K * shards). For a global top-100 across 16 shards you read 1,600 entries to pick 100. Many real systems shard by region or time window instead of by user, so each shard answers a self-contained query.