Skip to content

Redis Cache: In-Memory Data Store for High-Performance Caching

DodaTech Updated 2026-06-28 4 min read

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

  1. What is the difference between Redis eviction policies allkeys-lru and volatile-lru?
  2. How does Redis Cluster distribute keys across nodes?
  3. When would you use Redis Hash instead of a JSON string for caching?
  4. What is Redis pipelining and when should you use it?
  5. 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

What is the best Redis eviction policy for caching?

volatile-lru (evict keys with TTL set, using LRU) is generally best for caching. It ensures keys without TTL (like permanent configuration) are never evicted.

How do I set a TTL in Redis?

Use EXPIRE key seconds or set the TTL at creation with SETEX key seconds value. For hashes, call EXPIRE after HSET.

Should I enable Redis persistence for a cache?

For a pure cache, persistence is optional since data can be rebuilt. However, enabling AOF with everysec fsync provides a good balance of durability and performance if you want to avoid cold starts.

What is the difference between Redis and Memcached?

Redis supports rich data structures, persistence, replication, and clustering. Memcached is simpler (strings only) with lower memory overhead per key and multi-threaded architecture.

How do I monitor Redis cache hit ratio?

Use INFO stats command to see keyspace_hits and keyspace_misses. Calculate hit ratio = hits / (hits + misses). A ratio below 90% indicates the cache is too small or TTLs are too short.

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