Redis use cases.

1. Cache layer

def get_user(id):
    key = f"user:{id}"
    cached = redis.get(key)
    if cached: return json.loads(cached)
    user = db.user.find(id)
    redis.setex(key, 300, json.dumps(user))
    return user

2. Session store

sid = secrets.token_urlsafe(32)
redis.setex(f"session:{sid}", 86400 * 7, json.dumps({"user_id": uid}))

3. Rate limiter

def allow(key, limit, window):
    pipe = redis.pipeline()
    pipe.incr(key); pipe.expire(key, window)
    count, _ = pipe.execute()
    return count <= limit

4. Queue (Streams)

redis.xadd("jobs", {"task": "process", "id": 42})

# Worker
res = redis.xreadgroup("workers", "w1", {"jobs": ">"}, count=1, block=5000)

Or BullMQ / arq for full lib.

5. Leaderboard

redis.zadd("scores", {"alice": 100, "bob": 85})
top10 = redis.zrevrange("scores", 0, 9, withscores=True)
my_rank = redis.zrevrank("scores", "alice")

6. Real-time feed

# Add post to user's feed
def post(user_id, post_id, ts):
    for follower in get_followers(user_id):
        redis.lpush(f"feed:{follower}", post_id)
        redis.ltrim(f"feed:{follower}", 0, 999)        # keep top 1000

def get_feed(user_id, limit=20):
    return redis.lrange(f"feed:{user_id}", 0, limit - 1)

Fan-out on write. Or fan-out on read for celebrities.

7. Notifications

redis.lpush(f"notifs:{user_id}", json.dumps(notif))
redis.ltrim(f"notifs:{user_id}", 0, 99)
redis.publish(f"user:{user_id}", json.dumps(notif))  # if user online

8. Presence (online users)

def heartbeat(user_id):
    redis.zadd("online", {user_id: time.time()})

def cleanup():
    cutoff = time.time() - 60
    redis.zremrangebyscore("online", 0, cutoff)

def online_count():
    return redis.zcard("online")

Or bitmap by user_id.

9. Counter / analytics

def increment_view(post_id):
    redis.incr(f"views:{post_id}")

def daily_views(post_id, date):
    return redis.incr(f"views:{date.isoformat()}:{post_id}")

Persist to DB periodically.

10. Distributed lock

token = secrets.token_hex(16)
if redis.set(f"lock:{res}", token, nx=True, ex=30):
    try: ...
    finally:
        # Lua CAS release
        ...

11. Geofencing

redis.geoadd("drivers", lng, lat, driver_id)
nearby = redis.geosearch("drivers", longitude=user_lng, latitude=user_lat, radius=5, unit="km")

12. AB test bucketing

def variant(user_id, exp):
    bucket = redis.hget(f"exp:{exp}", user_id)
    if bucket: return bucket
    bucket = "A" if hash(f"{exp}:{user_id}") % 2 == 0 else "B"
    redis.hset(f"exp:{exp}", user_id, bucket)
    return bucket

13. Feature flags

def feature_enabled(feature, user_id):
    val = redis.hget(f"feature:{feature}", "config")
    config = json.loads(val) if val else {}
    if config.get("disabled"): return False
    if user_id in config.get("allowlist", []): return True
    if user_id in config.get("denylist", []): return False
    return user_id % 100 < config.get("percent", 0)

14. Deduplication (Bloom or set)

# Set
if redis.sadd("seen:events", event_id) == 0:
    return  # duplicate

# Bloom (memory efficient)
if redis.execute_command("BF.EXISTS", "seen", event_id):
    return
redis.execute_command("BF.ADD", "seen", event_id)

15. Cursor pagination tokens

def page(after=None, limit=20):
    items = redis.zrangebyscore("posts", "(" + (after or "-inf"), "+inf", start=0, num=limit)
    next_cursor = items[-1].score if items else None
    return items, next_cursor

16. Pub/sub for cache invalidation

def invalidate(key):
    redis.delete(key)
    redis.publish("invalidations", key)

# Other servers listen and clear local in-memory cache

17. Soft delete with TTL

redis.expire(f"user:{id}", 86400 * 30)  # purge after 30 days

18. URL shortener

def shorten(url):
    code = base62.encode(redis.incr("counter"))
    redis.set(f"url:{code}", url)
    return code
# Increment score with decay
redis.zincrby("trending", 1, item_id)
redis.expire("trending", 3600)
top = redis.zrevrange("trending", 0, 9)
# Index
for word in extract_words(doc):
    redis.sadd(f"idx:{word}", doc_id)

# Search
results = redis.sinter([f"idx:{w}" for w in query_words])

For real search: use RediSearch.

Common mistakes

  • Using Redis as primary DB for relational data.
  • Cache without invalidation strategy.
  • Streams without consumer groups for queue.
  • Lists growing unbounded (no LTRIM).
  • Forgetting persistence when treating it as a queue.

Read this next

If you want my Redis recipes library, it’s at rajpoot.dev .


Building something AI-, backend-, or data-heavy and want a second pair of eyes? I do consulting and freelance work — see my projects and ways to reach me at rajpoot.dev .