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
quantitywithout rewriting the whole cart JSON string. - Interview questions often pair "leaderboard" with sorted sets and "object cache" with hashes — know the command set cold.
Hashes: field-level objects
A hash stores field-value pairs under a single key. VaultCommerce keeps lightweight cart state per user:
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 —
HEXISTSchecks 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.
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:
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
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.
ZINCRBYatomically bumps a score;ZREVRANGE ... WITHSCORESreads top N.- Use timestamp scores with
ZRANGEBYSCOREfor 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.