Back to Database Engineering

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 redis
brew services start redis

Ubuntu/Debian:

sudo apt install redis-server
sudo systemctl start redis

Docker:

docker run -d -p 6379:6379 redis

Connecting 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!