Redis Cache: In-Memory Data Store for High-Performance Caching
In this tutorial, you will learn about Redis Cache: In. We cover key concepts, practical examples, and best practices to help you master this topic.
Redis is an in-memory data structure store that serves as an ideal cache backend. It supports strings, hashes, lists, sets, sorted sets, and streams, with built-in TTL, atomic operations, publish/subscribe, and Lua scripting for custom cache logic.
flowchart TB
App[Application] --> RP[Redis Client / Connection Pool]
RP --> RedisPrimary[Redis Primary]
RedisPrimary --> RedisReplica1[Replica 1]
RedisPrimary --> RedisReplica2[Replica 2]
RedisPrimary --> Sentinel1[Sentinel]
RedisPrimary --> Sentinel2[Sentinel]
RP -.->|Read Replicas| RedisReplica1
RP -.->|Read Replicas| RedisReplica2
RedisPrimary --> RDB[RDB Snapshot]
RedisPrimary --> AOF[Append-Only File]
What You'll Learn
- Redis data structures optimized for Caching
- TTL, expiry policies, and key eviction (allkeys-lru, volatile-ttl, etc.)
- Redis cluster, sentinel, and connection pooling for production
- Pipelining and batching for high-throughput cache operations
Why It Matters
Redis can handle millions of operations per second with sub-millisecond latency. As a cache, it offloads database queries, session storage, and rate-limit counters while providing built-in high availability and persistence.
Real-World Use
A social media platform caches user sessions, feed data, and notification counters in Redis. A single Redis cluster handles 500,000 reads/second with 99.9% cache hit rate. Session data has a 1-hour TTL; feed data uses sorted sets for pagination.
Redis Caching Patterns
Basic Cache-Aside with Redis
const redis = require('redis');
const { promisify } = require('util');
const client = redis.createClient({
url: process.env.REDIS_URL,
socket: { reconnectStrategy: (retries) => Math.min(retries * 50, 2000) }
});
async function getCachedOrFetch(key, fetchFn, ttl = 3600) {
const cached = await client.get(key);
if (cached) {
return JSON.parse(cached);
}
const data = await fetchFn();
await client.setEx(key, ttl, JSON.stringify(data));
return data;
}
Expected output:
First call: fetchFn executes, result cached in Redis with TTL. Subsequent calls within TTL: returns from Redis without fetchFn.
Redis Hash Cache for Structured Data
async function cacheUserProfile(userId, profile) {
await client.hSet(`user:${userId}`, {
name: profile.name,
email: profile.email,
avatar: profile.avatar,
lastLogin: profile.lastLogin.toISOString()
});
await client.expire(`user:${userId}`, 3600);
}
async function getCachedUserProfile(userId) {
const profile = await client.hGetAll(`user:${userId}`);
if (!profile || Object.keys(profile).length === 0) return null;
return profile;
}
Expected output:
User profile stored as a Redis hash (fields: name, email, avatar, lastLogin). Reading one field is faster than fetching entire JSON blob.
Connection Pooling with ioredis
const Redis = require('ioredis');
const cluster = new Redis.Cluster([
{ host: 'redis-node-1', port: 6379 },
{ host: 'redis-node-2', port: 6379 },
{ host: 'redis-node-3', port: 6379 }
], {
redisOptions: {
enableReadyCheck: true,
maxRetriesPerRequest: 3,
retryStrategy(times) {
return Math.min(times * 100, 3000);
}
}
});
async function getFeed(userId) {
const feedKey = `feed:${userId}`;
const exists = await cluster.exists(feedKey);
if (!exists) {
const posts = await generateFeed(userId);
const multi = cluster.multi();
posts.forEach((p, i) => multi.zAdd(feedKey, i, JSON.stringify(p)));
multi.expire(feedKey, 300);
await multi.exec();
}
return cluster.zRange(feedKey, 0, -1);
}
Expected output:
ioredis cluster distributes keys across shards. Feed is stored as a sorted set with 5-minute TTL. Commands are batched with multi/exec.
Common Mistakes
- Storing large objects (>10KB) in Redis without compression, wasting memory and increasing network latency.
- Not setting maxmemory policy, causing Redis to crash or block writes when memory is full.
- Using a single Redis instance in production without replicas or persistence, risking total data loss.
- Storing session data in Redis without configuring key eviction appropriately (use volatile-lru for sessions).
- Creating too many connections without pooling, exhausting the Redis connection limit (default 10,000).
Practice Questions
- What is the difference between Redis eviction policies allkeys-lru and volatile-lru?
- How does Redis Cluster distribute keys across nodes?
- When would you use Redis Hash instead of a JSON string for caching?
- What is Redis pipelining and when should you use it?
- How does RDB persistence differ from AOF persistence?
Challenge
Design a Redis caching layer for a real-time leaderboard. Use sorted sets to store player scores. Cache the top 100 leaderboard for 60 seconds. On score update, update the sorted set and invalidate the cached leaderboard. Handle 10,000 concurrent score updates with pipelining.
FAQ
Mini Project
Build a Redis-backed cache layer for the blog API. Implement cache-aside for posts, list caching with sorted sets for paginated post lists, and hash-based caching for user profiles. Add cache statistics endpoint showing hit ratio, memory usage, and eviction count.
What's Next
Continue with In-Memory Cache to explore language runtime caches and local caching patterns.
Built by the developers of DodaTech
Doda Browser, DodaZIP & Durga Antivirus Pro