What Is Redis?
Redis (Remote Dictionary Server) is an in-memory data store created by Salvatore Sanfilippo in 2009. Unlike traditional relational databases that read and write to disk, Redis stores data primarily in RAM, making it extraordinarily fast — capable of handling millions of operations per second with sub-millisecond latency.
Redis is most commonly used for:
- Caching — storing the results of expensive database queries or API calls
- Session storage — keeping user session data accessible across multiple servers
- Real-time leaderboards — sorted sets make ranking operations trivial
- Message queues — pub/sub and streams enable event-driven architectures
- Rate limiting — counting requests per user per time window
- Distributed locks — coordinating access to shared resources
Installation and Connection
Install Redis locally:
# macOS
brew install redis
brew services start redis
# Ubuntu/Debian
sudo apt update && sudo apt install redis-server
sudo systemctl start redis
# Docker (easiest)
docker run -d --name redis -p 6379:6379 redis:7-alpine
Connect using the Redis CLI:
redis-cli
127.0.0.1:6379> PING
PONG
Core Data Types
Redis is not just a simple key-value store. It supports rich data structures:
Strings
The simplest type — a key mapped to a string value (which can also hold integers):
SET user:1:name "Alice"
GET user:1:name # "Alice"
SET counter 0
INCR counter # 1
INCRBY counter 5 # 6
DECRBY counter 2 # 4
# Expiration
SET session:abc "token123" EX 3600 # Expires in 1 hour
TTL session:abc # Seconds remaining
PERSIST session:abc # Remove expiration
Lists
Ordered sequences — great for queues, recent activity feeds, and stacks:
LPUSH notifications "New message" # Push to left
RPUSH notifications "New follower" # Push to right
LRANGE notifications 0 -1 # Get all elements
LPOP notifications # Remove and return from left
LLEN notifications # Length
# Use as a queue: RPUSH to enqueue, LPOP to dequeue
# Use as a stack: LPUSH to push, LPOP to pop
# BLPOP blocks until an element is available (perfect for workers)
BLPOP notifications 0
Hashes
Key-value pairs within a single Redis key — perfect for representing objects:
HSET user:1 name "Alice" email "alice@example.com" age 30
HGET user:1 name # "Alice"
HGETALL user:1 # All fields and values
HSET user:1 age 31 # Update a single field
HINCRBY user:1 login_count 1 # Increment a numeric field
HDEL user:1 age # Delete a field
HEXISTS user:1 email # 1 (true)
Sets
Unordered collections of unique strings — great for tags, followers, and set operations:
SADD tags:post:1 "python" "redis" "backend"
SADD tags:post:2 "python" "django" "web"
SMEMBERS tags:post:1 # All members
SISMEMBER tags:post:1 "redis" # 1 (true)
SCARD tags:post:1 # Count: 3
# Set operations
SUNION tags:post:1 tags:post:2 # Union
SINTER tags:post:1 tags:post:2 # Intersection: {"python"}
SDIFF tags:post:1 tags:post:2 # Difference: {"redis", "backend"}
Sorted Sets (ZSets)
Each member has an associated floating-point score. Members are always sorted by score — perfect for leaderboards, rankings, and time-series data:
ZADD leaderboard 1500 "alice"
ZADD leaderboard 2300 "bob"
ZADD leaderboard 1800 "carol"
ZRANGE leaderboard 0 -1 WITHSCORES # Ascending order
ZREVRANGE leaderboard 0 2 WITHSCORES # Top 3 descending
ZRANK leaderboard "alice" # Rank (0-indexed)
ZSCORE leaderboard "bob" # 2300
ZINCRBY leaderboard 100 "alice" # Add 100 to Alice's score
Publish/Subscribe (Pub/Sub)
Redis pub/sub enables real-time messaging between clients:
# Terminal 1: Subscribe
redis-cli SUBSCRIBE news sports
# Terminal 2: Publish
redis-cli PUBLISH news "Breaking: Redis 8.0 released"
# Terminal 1 will receive this message
In Node.js:
const redis = require('redis');
const subscriber = redis.createClient();
const publisher = redis.createClient();
await subscriber.connect();
await publisher.connect();
await subscriber.subscribe('notifications', (message, channel) => {
console.log(`Received on ${channel}: ${message}`);
});
await publisher.publish('notifications', JSON.stringify({ type: 'alert', text: 'Server overloaded' }));
Practical Use Case: Caching
The most common Redis use case is caching database query results:
import redis
import json
import hashlib
from functools import wraps
r = redis.Redis(host='localhost', port=6379, db=0, decode_responses=True)
def cache_result(ttl=300):
"""Cache function results in Redis for ttl seconds."""
def decorator(func):
@wraps(func)
def wrapper(*args, **kwargs):
# Create cache key from function name and arguments
key_data = f"{func.__name__}:{args}:{kwargs}"
cache_key = hashlib.md5(key_data.encode()).hexdigest()
# Try cache first
cached = r.get(cache_key)
if cached:
return json.loads(cached)
# Cache miss — call the function
result = func(*args, **kwargs)
r.setex(cache_key, ttl, json.dumps(result))
return result
return wrapper
return decorator
@cache_result(ttl=600) # Cache for 10 minutes
def get_user_profile(user_id):
# Expensive database query
return db.query("SELECT * FROM users WHERE id = ?", user_id)
Rate Limiting with Redis
A simple but effective sliding window rate limiter:
def is_rate_limited(user_id, limit=100, window=60):
"""Allow up to `limit` requests per `window` seconds."""
key = f"rate:{user_id}"
pipe = r.pipeline()
pipe.incr(key)
pipe.expire(key, window)
result = pipe.execute()
return result[0] > limit
Persistence Options
Redis is not just a volatile cache — it offers durability:
- RDB (Redis Database): Periodic snapshots of the dataset. Fast restarts, some data loss possible.
- AOF (Append Only File): Logs every write operation. Minimal data loss, slower restart.
- RDB + AOF: Use both for best of both worlds.
Configure in redis.conf:
save 900 1 # Save if at least 1 key changed in 900 seconds
appendonly yes # Enable AOF
appendfsync everysec # Sync AOF to disk every second
Redis Cluster and High Availability
For production:
- Redis Sentinel: Monitors Redis instances and performs automatic failover
- Redis Cluster: Shards data across multiple nodes for horizontal scaling
- Managed Redis: Use ElastiCache (AWS), Upstash, or Redis Cloud to avoid operational overhead
Connecting from Python and Node.js
# Python
import redis
r = redis.Redis(host='localhost', port=6379, decode_responses=True)
r.set('key', 'value')
print(r.get('key'))
// Node.js
const { createClient } = require('redis');
const client = createClient({ url: 'redis://localhost:6379' });
await client.connect();
await client.set('key', 'value');
console.log(await client.get('key'));
Conclusion
Redis's combination of speed, versatility, and rich data structures makes it one of the most valuable tools in a developer's toolkit. Start with simple caching to improve application performance, then explore sorted sets for leaderboards, streams for event sourcing, and pub/sub for real-time features. The investment in learning Redis pays off quickly.