Module 4: Redis & Caching
Master Redis for high-performance caching, session management, and real-time applications.
What is Redis?
Redis (Remote Dictionary Server) is like a super-fast sticky note board in your computer's memory. It stores data in RAM, making it incredibly fast - perfect for caching, sessions, and real-time features. Used by Twitter, GitHub, Stack Overflow, and millions of applications.
⚡ Why Redis?
- Blazing Fast: Sub-millisecond response times
- In-Memory: Data stored in RAM for speed
- Rich Data Structures: Strings, lists, sets, hashes, sorted sets
- Persistence Options: Can save to disk for durability
- Pub/Sub: Built-in messaging system
- Simple: Easy to learn and use
🎯 Common Use Cases:
- • Caching: Store frequently accessed data
- • Session Storage: User sessions in web apps
- • Rate Limiting: API request throttling
- • Leaderboards: Gaming scores, rankings
- • Real-time Analytics: Counters, metrics
- • Message Queues: Background job processing
Redis Setup
macOS (using Homebrew):
brew install redisbrew services start redisUbuntu/Debian:
sudo apt install redis-serversudo systemctl start redisDocker:
docker run -d -p 6379:6379 redisConnecting to Redis
# Start Redis CLI
redis-cli
# Test connection
PING # Should return PONG
# Get server info
INFO
Redis Data Structures
1. Strings
The simplest data type. Can store text, numbers, or binary data up to 512MB.
# Set and get
SET user:1:name "John Doe"
GET user:1:name
# Set with expiration (in seconds)
SETEX session:abc123 3600 "user_data"
# Increment/Decrement
SET views:post:1 0
INCR views:post:1 # Returns 1
INCRBY views:post:1 10 # Returns 11
# Multiple operations
MSET key1 "value1" key2 "value2"
MGET key1 key2
2. Hashes
Like objects/dictionaries - perfect for storing structured data like user profiles.
# Set hash fields
HSET user:1 name "John" email "john@example.com" age 30
# Get single field
HGET user:1 name
# Get all fields
HGETALL user:1
# Increment hash field
HINCRBY user:1 age 1
# Check if field exists
HEXISTS user:1 email
3. Lists
Ordered collections - great for queues, activity feeds, recent items.
# Push to list (left/right)
LPUSH notifications "New message"
RPUSH notifications "Friend request"
# Pop from list
LPOP notifications
RPOP notifications
# Get range
LRANGE notifications 0 9 # First 10 items
# List length
LLEN notifications
# Trim list (keep only range)
LTRIM notifications 0 99 # Keep latest 100
4. Sets
Unordered collections of unique values - perfect for tags, unique visitors.
# Add to set
SADD tags:post:1 "redis" "caching" "database"
# Check membership
SISMEMBER tags:post:1 "redis"
# Get all members
SMEMBERS tags:post:1
# Set operations
SINTER tags:post:1 tags:post:2 # Intersection
SUNION tags:post:1 tags:post:2 # Union
SDIFF tags:post:1 tags:post:2 # Difference
5. Sorted Sets
Sets with scores - ideal for leaderboards, rankings, priority queues.
# Add with scores
ZADD leaderboard 100 "player1" 250 "player2" 180 "player3"
# Get rank (0-based)
ZRANK leaderboard "player1"
# Get top players (highest scores)
ZREVRANGE leaderboard 0 9 WITHSCORES
# Increment score
ZINCRBY leaderboard 50 "player1"
# Get by score range
ZRANGEBYSCORE leaderboard 100 200
Caching Patterns
Caching is like keeping frequently used items on your desk instead of in a filing cabinet. Here are the most common patterns:
Cache-Aside (Lazy Loading)
Application checks cache first, loads from database if miss, then stores in cache.
async function getUser(userId) {
// Try cache first
let user = await redis.get(`user:${userId}`);
if (user) {
return JSON.parse(user); // Cache hit
}
// Cache miss - load from database
user = await db.users.findById(userId);
// Store in cache for 1 hour
await redis.setex(`user:${userId}`, 3600, JSON.stringify(user));
return user;
}
Write-Through Cache
Write to cache and database simultaneously - ensures cache is always up-to-date.
async function updateUser(userId, data) {
// Update database
const user = await db.users.update(userId, data);
// Update cache
await redis.setex(`user:${userId}`, 3600, JSON.stringify(user));
return user;
}
Cache Invalidation
Remove stale data from cache when it changes.
async function deleteUser(userId) {
// Delete from database
await db.users.delete(userId);
// Invalidate cache
await redis.del(`user:${userId}`);
}
💡 Caching Best Practices:
- • Set appropriate TTL (Time To Live) for cached data
- • Cache frequently accessed, rarely changed data
- • Use cache keys with namespaces (e.g., "user:123")
- • Handle cache failures gracefully
- • Monitor cache hit/miss ratios
- • Avoid caching user-specific data globally
Session Management
Redis is perfect for storing user sessions - fast access and automatic expiration.
// Express.js with Redis sessions
const session = require('express-session');
const RedisStore = require('connect-redis').default;
const redis = require('redis');
const redisClient = redis.createClient();
app.use(session({
store: new RedisStore({ client: redisClient }),
secret: 'your-secret-key',
resave: false,
saveUninitialized: false,
cookie: {
secure: true,
maxAge: 24 * 60 * 60 * 1000 // 24 hours
}
}));
// Use session
app.post('/login', (req, res) => {
req.session.userId = user.id;
res.json({ success: true });
});
Pub/Sub Messaging
Redis Pub/Sub enables real-time messaging between different parts of your application.
// Publisher
const redis = require('redis');
const publisher = redis.createClient();
publisher.publish('notifications', JSON.stringify({
type: 'new_message',
userId: 123,
message: 'Hello!'
}));
// Subscriber
const subscriber = redis.createClient();
subscriber.subscribe('notifications');
subscriber.on('message', (channel, message) => {
const data = JSON.parse(message);
console.log('Received:', data);
});
Rate Limiting
Protect your API from abuse by limiting requests per user/IP.
// Simple rate limiter: 100 requests per hour
async function checkRateLimit(userId) {
const key = `ratelimit:${userId}:${Date.now() / 3600000 | 0}`;
// Increment counter
const count = await redis.incr(key);
// Set expiration on first request
if (count =>= 1) {
await redis.expire(key, 3600);
}
// Check limit
if (count > 100) {
throw new Error('Rate limit exceeded');
}
return true;
}
Using Redis with Node.js
// Install Redis client
npm install redis
// Connect to Redis
const redis = require('redis');
const client = redis.createClient({
host: 'localhost',
port: 6379
});
client.on('error', (err) => console.error('Redis error:', err));
await client.connect();
// Basic operations
await client.set('key', 'value');
const value = await client.get('key');
// With expiration
await client.setEx('session:123', 3600, 'user_data');
// Hash operations
await client.hSet('user:1', 'name', 'John');
const name = await client.hGet('user:1', 'name');
Redis Performance Tips
✅ Do
- • Use pipelining for multiple commands
- • Set appropriate TTL on all keys
- • Use connection pooling
- • Monitor memory usage
- • Use Redis Cluster for scaling
- • Compress large values
❌ Don't
- • Don't use KEYS in production
- • Don't store huge values (>1MB)
- • Don't use Redis as primary database
- • Don't forget to handle disconnections
- • Don't ignore maxmemory settings
- • Don't use blocking operations
🛠️ Hands-On Project: E-commerce Cache Layer
Build a caching layer for an e-commerce site with product caching, session management, and rate limiting.
Project Requirements:
- ✓ Cache product details with 1-hour TTL
- ✓ Cache-aside pattern for database queries
- ✓ Session storage for shopping carts
- ✓ Rate limiting for API endpoints
- ✓ Real-time inventory updates via Pub/Sub
- ✓ Leaderboard for best-selling products
- ✓ Cache invalidation on product updates
📚 Module Summary
You've mastered Redis and caching strategies:
- ✓ Redis data structures and commands
- ✓ Caching patterns and best practices
- ✓ Session management
- ✓ Pub/Sub messaging
- ✓ Rate limiting techniques
- ✓ Node.js integration
Next: Learn data modeling and database design principles!