Redis: The Multi-Tool You're Probably Not Using Enough


Hey there! So you're using Redis as a cache, maybe even as a task queue. That's cool... but we need to talk. You might be missing out on one of the most versatile tools in your development toolkit.
What Redis Really Is (Beyond Just a Cache)
Let's be honest—most of us started using Redis because someone told us "it's fast" or "use it for caching." And they weren't wrong! But Redis is like that Swiss Army knife you bought thinking you'd only use the blade, only to discover there's a whole world of tools folded inside.
Redis is an in-memory data structure store that can act as:
A lightning-fast cache (obviously)
A message broker for job queues
A real-time leaderboard engine
A session store that won't crash your app
A rate-limiter for your APIs
The magic of Redis lies in how it combines simplicity with versatility. It's essentially a server that manipulates data structures over a network—but does it so blazingly fast that it feels like you're just working with local data structures.
Why Redis Feels So Natural for Python Devs
One thing I love about Redis? It feels like using Python's native data types, but over the network.
Think about it:
Redis strings = Python variables
Redis hashes = Python dictionaries
Redis lists = Python lists (with push/pop operations)
Once you get used to it, Redis feels like working with Python dicts or lists — most commands just make sense.
Redis Core Operations Explained
Before diving into code, let's break down some essential Redis commands you'll use daily:
String Operations
SET key value # Store a string value
GET key # Retrieve a string value
DEL key # Delete a key
EXPIRE key seconds # Set a key to expire after X seconds
Hash Operations (Think Dictionaries)
HSET user:1 name "John" # Set a field in a hash
HGET user:1 name # Get a field value
HGETALL user:1 # Get all field-value pairs
HDEL user:1 name # Delete a field
List Operations (Think Queues and Stacks)
LPUSH mylist "item" # Push to left (front) of list
RPUSH mylist "item" # Push to right (end) of list
LPOP mylist # Pop from left (front) of list
RPOP mylist # Pop from right (end) of list
LRANGE mylist 0 -1 # Get all elements (0 to last)
Here's how to think about list operations:
LPUSH
+RPOP
= queue (first in, first out)LPUSH
+LPOP
= stack (last in, first out)RPUSH
+LPOP
= queue in reverse order
Set and Sorted Set Operations
SADD myset "value" # Add to a set (no duplicates)
SMEMBERS myset # Get all set members
SISMEMBER myset "value" # Check if value exists in set
ZADD scores 100 "player1" # Add to sorted set with score
ZRANGE scores 0 -1 # Get all members (by score, ascending)
ZREVRANGE scores 0 -1 # Get all members (by score, descending)
The power of these operations comes from their simplicity and atomic nature. Redis handles all the locking and concurrency issues for you, so these operations are thread-safe without any extra work on your part.
Setting Up Async Redis in Python (Without the Complexity)
Before we dive into code, let's understand why async Redis matters. In modern web apps, you don't want Redis operations blocking your event loop, especially for high-traffic APIs or real-time systems.
Luckily, the official Redis Python library now has excellent async support. Here's how to get started:
# Install the library with async support
pip install redis[async]
Now, let's create a simple helper class that wraps common Redis operations:
# redis_client.py
import redis.asyncio as redis
class RedisClient:
def __init__(self, host="localhost", port=6379, db=0):
self.client = redis.Redis(host=host, port=port, db=db, decode_responses=True)
async def set_value(self, key: str, value: str):
await self.client.set(key, value)
async def get_value(self, key: str) -> str:
return await self.client.get(key)
async def hset_value(self, hash_key: str, field: str, value: str):
await self.client.hset(hash_key, field, value)
async def hget_value(self, hash_key: str, field: str) -> str:
return await self.client.hget(hash_key, field)
async def lpush_value(self, list_key: str, value: str):
await self.client.lpush(list_key, value)
async def lpop_value(self, list_key: str) -> str:
return await self.client.lpop(list_key)
Using this helper is straightforward:
# main.py
import asyncio
from redis_client import RedisClient
async def main():
redis_client = RedisClient()
# Simple key-value store
await redis_client.set_value("name", "John")
print(await redis_client.get_value("name")) # John
# Working with a hash (think nested dictionary)
await redis_client.hset_value("user:1", "email", "john@example.com")
print(await redis_client.hget_value("user:1", "email"))
# Using lists as queues
await redis_client.lpush_value("task_queue", "task_1")
print(await redis_client.lpop_value("task_queue")) # task_1
asyncio.run(main())
Reference Guide: 3 Redis Patterns for Everyday Use for common use
Let's move beyond theory and look at practical examples where Redis shines.
1. Building a Priority Notification Queue
Imagine you're sending notifications to users, and some are more urgent than others:
async def queue_notification(user_id, message, priority="normal"):
notification = json.dumps({
"user_id": user_id,
"message": message,
"timestamp": time.time()
})
# High priority notifications go to the front of their queue
if priority == "high":
await redis_client.lpush_value("notifications:high", notification)
else:
await redis_client.lpush_value("notifications:normal", notification)
Your worker process can then prioritize processing:
async def process_notifications():
while True:
# Always check high priority first
notification = await redis_client.rpop_value("notifications:high")
if not notification:
# Fall back to normal queue
notification = await redis_client.rpop_value("notifications:normal")
if notification:
data = json.loads(notification)
await send_push_notification(data["user_id"], data["message"])
else:
# No work, brief pause before checking again
await asyncio.sleep(0.1)
2. Real-time Leaderboards in 10 Lines of Code
Redis Sorted Sets make leaderboards almost trivially easy:
async def update_score(user_id, score):
# This automatically sorts users by score
await redis_client.client.zadd("leaderboard", {user_id: score})
async def get_top_players(count=10):
# Get top scores in descending order (highest first)
return await redis_client.client.zrevrange("leaderboard", 0, count-1, withscores=True)
async def get_player_rank(user_id):
# Find a specific player's position
rank = await redis_client.client.zrevrank("leaderboard", user_id)
return rank + 1 if rank is not None else None
3. Simple API Rate Limiting
Need to limit how many requests a user can make? Redis handles this beautifully:
async def rate_limit(user_id, limit=100, period=3600):
"""Limit users to {limit} requests per {period} seconds"""
current_time = int(time.time())
key = f"ratelimit:{user_id}"
# Remove expired requests from our window
window_start = current_time - period
await redis_client.client.zremrangebyscore(key, 0, window_start)
# Count current requests
request_count = await redis_client.client.zcard(key)
if request_count < limit:
# Track this request with timestamp
await redis_client.client.zadd(key, {str(current_time): current_time})
await redis_client.client.expire(key, period) # Auto-cleanup
return True # Request allowed
else:
return False # Rate limit exceeded
When to Use Redis (and When Not To)
Redis is fantastic, but it's not the answer to every problem. Here's my rule of thumb:
Use Redis when:
Speed is crucial (sub-millisecond responses)
Your access patterns are simple and predictable
You're building real-time features
You need atomic operations on data structures
Your data fits comfortably in memory
Consider alternatives when:
Your data is larger than available memory
You need complex SQL-like queries
You require ACID transactions
Data durability is your top priority
Final Byte
Redis isn’t just fast — it’s versatile. Whether you’re caching data, queuing tasks, or building real-time features, Redis often has your back in more ways than you think.
It’s one of those rare tools that helps you focus on solving problems, not fighting infrastructure.
Until next time, happy building! 🚀
Subscribe to my newsletter
Read articles from Akhilesh Thykkat directly inside your inbox. Subscribe to the newsletter, and don't miss out.
Written by

Akhilesh Thykkat
Akhilesh Thykkat
Hey, I’m Akhilesh M T, a backend engineer who loves learning and building scalable systems while diving into system design. When I’m not coding, I’m usually traveling, exploring new places, or reading up on the latest tech. Always learning, always optimizing, and looking for new challenges—whether in tech or in life.