PrepZone Logo
PrepZone

Redis Sorted Sets and Hashes

Leaderboards with ZADD, user profiles with HSET — structured data in memory.

Why this matters

  • VaultCommerce bestseller rankings and flash-sale leaderboards need ordered retrieval by score — sorted sets do this in O(log N).
  • User profiles and cart summaries change one field at a time; hashes let you update quantity without rewriting the whole cart JSON string.
  • Interview questions often pair "leaderboard" with sorted sets and "object cache" with hashes — know the command set cold.
StringSessions, counters
ListQueues, feeds
SetUnique tags
Sorted SetLeaderboards
HashUser profiles
StreamEvent log
Each type maps to a common backend pattern.

Hashes: field-level objects

A hash stores field-value pairs under a single key. VaultCommerce keeps lightweight cart state per user:

Java
HSET cart:42 SKU-8842 quantity 2 price 89
HSET cart:42 SKU-1100 quantity 1 price 45
HGET cart:42 SKU-8842
HINCRBY cart:42 SKU-8842 quantity 1
HGETALL cart:42
HDEL cart:42 SKU-1100
EXISTS cart:42

When hashes beat JSON strings

  • Partial updates — change one SKU quantity without parsing and rewriting JSON.
  • Memory efficiency — Redis encodes small hashes compactly (ziplist/listpack thresholds).
  • Field existence — HEXISTS checks a single attribute without loading the full object.

For large nested objects (full product catalog entries), a cached JSON string is still fine. Hashes shine when you mutate individual fields frequently.

Sorted sets: ranked bestsellers

Each member has a unique name and a double score. Redis keeps members sorted by score, then lexicographically for ties.

Java
ZADD bestsellers:daily 8420 "SKU-8842" 7100 "SKU-3310" 9100 "SKU-9901"
ZREVRANGE bestsellers:daily 0 4 WITHSCORES
ZINCRBY bestsellers:daily 150 "SKU-8842"
ZRANK bestsellers:daily "SKU-8842"
ZSCORE bestsellers:daily "SKU-8842"
ZREM bestsellers:daily "SKU-3310"

VaultCommerce refreshes the daily board every hour from Postgres sales totals, but real-time ZINCRBY during flash events gives merchandising a live pulse.

Range queries and time windows

Sorted sets also model time-ordered data when the score is a Unix timestamp:

Java
ZADD orders:pending 1710000000 "order-9001" 1710000060 "order-9002"
ZRANGEBYSCORE orders:pending 1710000000 1710003600
ZREMRANGEBYSCORE orders:pending -inf 1709996400

Expired pending orders drop off with ZREMRANGEBYSCORE instead of scanning every key in the namespace.

Python: cart hash + leaderboard

Java
import redis

r = redis.Redis(decode_responses=True)

def add_to_cart(user_id: int, sku: str, qty: int, price: float) -> None:
    key = f"cart:{user_id}"
    r.hset(key, mapping={f"{sku}:qty": qty, f"{sku}:price": price})

def top_products(board: str, n: int = 5) -> list[tuple[str, float]]:
    return r.zrevrange(f"bestsellers:{board}", 0, n - 1, withscores=True)

Quick recall

Everything you need if you only revisit this box.

  • Hashes store field-value maps — ideal for carts and profiles with partial updates.
  • Sorted sets pair members with scores for leaderboards and time-ordered indexes.
  • ZINCRBY atomically bumps a score; ZREVRANGE ... WITHSCORES reads top N.
  • Use timestamp scores with ZRANGEBYSCORE for time-window cleanup.
  • JSON strings suit static blobs; hashes suit frequently mutated fields.
  • VaultCommerce uses sorted sets for daily bestsellers and pending-order sweeps.

Test yourself

Answer these before moving on — recall is what makes it stick.