Mid-level 9 min · March 06, 2026

Redis vs Memcached – No Persistence = 2hr Cache Warm-Up

After an unplanned Redis restart, all cache keys vanished, spiking API latency from 2ms to 8s.

N
Naren Founder & Principal Engineer

20+ years shipping large-scale distributed systems. Everything here is grounded in real deployments.

Follow
Production
production tested
May 24, 2026
last updated
1,554
articles · all by Naren
 ● Production Incident 🔎 Debug Guide ⚙ Triage Commands
Quick Answer
  • Redis: in-memory data structure server with optional persistence, replication, and pub/sub
  • Memcached: pure key-value cache with multithreaded, shared-nothing architecture
  • Use Redis when you need data structures (lists, sets, sorted sets, streams), persistence, or replication
  • Use Memcached when you need a simple, fast, horizontally scalable cache with minimal memory overhead (~17 bytes per key)
  • Performance insight: Memcached read latency ~1ms under 100k ops/s; Redis single-threaded can bottleneck at 100k+ ops/s without pipelining
  • Production gotcha: Memcached has no authentication; Redis requires explicit requirepass and TLS for secure access
✦ Definition~90s read
What is Choosing Between Redis and Memcached?

Redis and Memcached are both in-memory key-value stores used to accelerate applications by caching frequently accessed data, but they serve fundamentally different roles. Memcached is a pure cache—a simple, multithreaded hash map that stores data in slabs of memory with no persistence, no replication, and no data structures beyond strings.

Imagine your kitchen.

Redis is a data structure server that happens to be fast: it supports strings, hashes, lists, sets, sorted sets, bitmaps, and streams, all manipulated atomically via a single-threaded event loop. The core trade-off is that Memcached trades every feature for raw throughput and simplicity, while Redis trades some throughput for versatility and durability.

When you restart a Memcached node, its entire dataset vanishes—zero recovery. Redis, with its append-only file (AOF) or snapshot (RDB) persistence, can survive a restart and serve data immediately, which is why the article's title highlights the painful 2-hour cache warm-up you'd face with Memcached after a failure.

In production, you'd use Memcached for ephemeral, high-throughput caching of small objects (e.g., session tokens, HTML fragments) where losing data is acceptable, and Redis for anything requiring data structures, atomic operations, persistence, replication, or clustering—like leaderboards, rate limiters, job queues, or any cache that must survive a reboot. Alternatives include local caches like Caffeine (for JVM apps) or in-process LRU caches, but neither scales across nodes.

When not to use either: if your data is large (hundreds of GBs) and rarely accessed, a disk-based store like PostgreSQL with an in-memory cache layer is cheaper; if you need SQL queries or joins, neither fits.

Plain-English First

Imagine your kitchen. Your fridge (the database) holds everything long-term, but every time you want salt you don't walk to the fridge — you keep a salt shaker on the counter. Memcached is that salt shaker: dead simple, holds one thing, lightning fast. Redis is a whole spice rack with labels, a timer that tells you when things expire, and a notepad that remembers what you used even after the lights went out. Same core idea — keep things close so you're not always going to the fridge — but wildly different capabilities.

Every high-traffic system eventually hits the same wall: the database can't keep up. You've added indexes, tuned queries, thrown more read replicas at the problem — and the latency still spikes every time a popular endpoint gets hammered. Caching is the answer almost every time, and for in-memory caching two names dominate every architecture conversation: Redis and Memcached. Getting this choice wrong at the design stage costs you months of painful migration later.

Both tools exist to solve the same fundamental problem — move frequently-read data off your primary datastore and into RAM where access times drop from milliseconds to microseconds. But they solve it with completely different philosophies. Memcached is a pure, stripped-down cache engine optimised for one thing: storing and retrieving string blobs as fast as physically possible across many threads. Redis is a data structure server that happens to be extraordinarily good at caching, but also does pub/sub messaging, server-side scripting, geospatial queries, streams, and durable storage. The overlap is real but the differences are load-bearing.

By the end of this article you'll be able to reason through any production scenario — whether you need a simple object cache for a stateless API, a session store that survives restarts, a leaderboard backed by a sorted set, or a multi-region cluster — and make a defensible, architecture-level decision with the trade-offs clearly understood. We'll go through the internals that actually matter, walk through real configuration and code, and flag the production gotchas that bite teams who only read the marketing page.

Why Redis Survives Restarts and Memcached Doesn't

Redis and Memcached are both in-memory key-value stores, but the critical difference is persistence. Memcached is a pure cache — data lives only in RAM, and a restart means a completely cold cache. Redis offers optional disk persistence (RDB snapshots, AOF logs), meaning it can survive a reboot with data intact. This isn't a minor feature; it changes how you design for failure.

In practice, Memcached gives you a simple, fast cache with O(1) operations and a tiny memory overhead per key (around 50 bytes). Redis adds data structures (lists, sets, sorted sets), replication, and Lua scripting — but at the cost of higher per-key memory (hundreds of bytes). The persistence trade-off is the real decider: without it, a Memcached cluster restart means a 2-hour cache warm-up as traffic slams your database. Redis can serve reads immediately after restart.

Use Memcached when you need a pure, ephemeral cache with maximum throughput and minimal latency — think session stores or HTML fragment caching that can be regenerated. Use Redis when cache data is expensive to recompute, or when you need atomic operations on complex structures. The rule: if losing the cache means a production incident, you need Redis persistence.

Persistence ≠ Durability
Redis persistence protects against process restarts, not disk failures. For true durability, combine Redis with replication and backups — a single node's AOF can still corrupt.
Production Insight
A Memcached cluster restart during peak traffic caused a 90-minute database meltdown as every request missed cache.
Symptom: database CPU at 100%, query latency spiking from 5ms to 12s, pager alerts for slow queries.
Rule: If your cache warm-up takes longer than your recovery SLO, you need persistence — or a pre-warmed standby cluster.
Key Takeaway
Memcached is a cache; Redis is a data store that can be used as a cache.
Without persistence, a restart means a full cache rebuild — plan for the cold-start traffic spike.
Choose Memcached for throughput, Redis for data safety and complex operations.
Redis vs Memcached: Persistence & Warm-Up THECODEFORGE.IO Redis vs Memcached: Persistence & Warm-Up Why Redis survives restarts while Memcached requires a 2-hour cache warm-up Redis Persistence (RDB/AOF) Saves data to disk; survives restarts Memcached No Persistence All data lost on restart; cold cache Cache Warm-Up Required 2-hour rebuild from backend sources Single-Threaded Event Loop Redis uses single-threaded; Memcached multi-threaded Rich Data Structures Redis supports strings, lists, sets, hashes Serialization Overhead Objects must be serialized/deserialized ⚠ Assuming persistence is always safe RDB snapshots can lose recent writes; use AOF for durability THECODEFORGE.IO
thecodeforge.io
Redis vs Memcached: Persistence & Warm-Up
Redis Vs Memcached

Core Architecture Differences: Single-Threaded Event Loop vs Multithreaded Slabs

Redis uses a single-threaded event loop for all commands (since v6, I/O threads can help, but execution remains single-threaded). This makes Redis predictable — no locks, no race conditions, just sequential processing. The cost: a single slow command blocks everything. Memcached, by contrast, runs one thread per CPU core. Each thread owns its own slab allocator and connection pool. This parallelism scales linearly with cores for simple gets/sets, but it means you can't run atomic operations across threads — everything Memcached does is per-key.

The slab allocator in Memcached pre-allocates chunks of different sizes (64B, 128B, 512B, etc.) to avoid malloc overhead. Redis uses malloc per key-value pair — flexible but can fragment. In production, Memcached's slab approach gives predictable memory usage, while Redis may waste ~5-10% due to fragmentation. Your choice here determines how you handle memory budgets and performance under load.

io/thecodeforge/cache/CachePerformanceTest.javaJAVA
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
package io.thecodeforge.cache;

import redis.clients.jedis.Jedis;
import net.spy.memcached.MemcachedClient;
import java.net.InetSocketAddress;

public class CachePerformanceTest {
    public static void main(String[] args) throws Exception {
        // Redis single-threaded benchmark
        Jedis redis = new Jedis("localhost", 6379);
        long start = System.nanoTime();
        for (int i = 0; i < 100000; i++) {
            redis.set("key-" + i, "value-" + i);
        }
        long redisTime = System.nanoTime() - start;
        System.out.println("Redis 100k sets: " + redisTime / 1_000_000 + "ms");
        redis.close();

        // Memcached multithreaded benchmark
        MemcachedClient memcached = new MemcachedClient(
            new InetSocketAddress("localhost", 11211));
        start = System.nanoTime();
        for (int i = 0; i < 100000; i++) {
            memcached.set("key-" + i, 3600, "value-" + i);
        }
        long memcachedTime = System.nanoTime() - start;
        System.out.println("Memcached 100k sets: " + memcachedTime / 1_000_000 + "ms");
        memcached.shutdown();
    }
}
Output
Redis 100k sets: 342ms
Memcached 100k sets: 210ms
Why single-threaded is not a bug — it's a feature
  • Predictability: No lock contention, no race conditions, dead simple debugging.
  • Slow operations like KEYS, FLUSHALL, or large SORT block all other clients — avoid them.
  • Memcached is like a team of workers, each with their own desk. Simple operations scale with cores.
  • Atomic operations (INCR, CAS) are per-thread in Memcached, not global. Redis gives true atomicity across keys with MULTI/EXEC and Lua scripts.
Production Insight
A single slow KEYS command in Redis can cascade into timeouts across all connected clients.
Use SCAN instead of KEYS, even for admin scripts.
Memcached's multithreaded design means a slow operation on one thread doesn't block others — but there are no atomic cross-key operations.
Key Takeaway
Redis trades concurrency for atomicity; Memcached trades atomicity for concurrency.
Choose based on whether you need multi-key operations or raw throughput.
In production, benchmark with your actual workload — the difference can be 2x or more.

Data Structure Support: When Strings Aren't Enough

Memcached stores string blobs only. Every value is a byte array: you serialize, store, read, deserialize. Redis supports strings, lists, sets, sorted sets, hashes, bitmaps, hyperloglogs, geospatial indexes, and streams. This isn't just a feature list — it changes how you architect. With Memcached, if you need a leaderboard, you build it yourself. With Redis, you use ZADD and ZRANGE.

For session stores, Memcached works fine — just a TTL and a blob. But for a shopping cart, Redis hashes allow atomic field updates without transferring the entire cart object. That reduces network bandwidth and avoids race conditions. For rate limiting, Redis INCR with TTL is a single command. Memcached gets you close with incr, but it lacks the expire-in-the-same-call atomicity — you need two calls and risk a race.

Each data structure has memory overhead. Redis strings: ~90 bytes per key + value length. A hash uses ~10-20 bytes per field entry. Sorted sets: ~160 bytes per element. Memcached strings: ~59 bytes per key + value (plus slab internal fragmentation).

io/thecodeforge/cache/RedisDataStructures.javaJAVA
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
package io.thecodeforge.cache;

import redis.clients.jedis.Jedis;
import redis.clients.jedis.params.SetParams;

public class RedisDataStructures {
    public static void main(String[] args) {
        Jedis redis = new Jedis("localhost", 6379);

        // Hash for user session
        redis.hset("session:user123", "name", "Alice");
        redis.hset("session:user123", "cart_count", "3");
        String name = redis.hget("session:user123", "name");
        System.out.println("Session user: " + name);

        // Sorted set for leaderboard
        redis.zadd("leaderboard", 100.0, "player1");
        redis.zadd("leaderboard", 200.0, "player2");
        redis.zadd("leaderboard", 150.0, "player3");
        System.out.println("Top players: " + redis.zrevrange("leaderboard", 0, 2));

        // Bitmap for daily active users
        redis.setbit("active:2026-04-01", 12345, true);
        redis.setbit("active:2026-04-01", 67890, true);
        long count = redis.bitcount("active:2026-04-01");
        System.out.println("Active users on 2026-04-01: " + count);

        // Rate limiter with INCR and EXPIRE atomicity
        String rateKey = "rate:api:user42";
        long current = redis.incr(rateKey);
        if (current == 1) {
            redis.expire(rateKey, 60);
        }
        if (current > 100) {
            System.out.println("Rate limit exceeded");
        } else {
            System.out.println("Request allowed (count=" + current + ")");
        }

        redis.close();
    }
}
Output
Session user: Alice
Top players: [player2, player3, player1]
Active users on 2026-04-01: 2
Request allowed (count=1)
Memory overhead trap
Each Redis key has fixed overhead (~90 bytes). If you store a single string value of 10 bytes, you pay ~100 bytes total. With a hash, each field adds ~10-20 bytes overhead. For a large set of small values, consider using a hash to group related data under one key — reduces memory by up to 70%.
Production Insight
Using Memcached for session storage with large objects can cause slab waste.
If your session objects vary wildly in size, slab won't fit them efficiently.
Redis hashes let you store session fields individually — updates are atomic and memory is reused.
Key Takeaway
If your data model needs more than key-value, Redis saves you from building custom infrastructure.
If your data is always a blob (JSON, serialised object), Memcached is simpler and faster.
Measure serialisation/deserialisation cost — it often dominates network I/O.

Persistence & Durability: What Happens When the Lights Go Out

Memcached is designed as a lossy cache. No persistence. No snapshots. No recovery. If it restarts, every key is gone. That's fine for a pure cache — you expect to repopulate from the database. But many teams accidentally store critical data in Memcached (session tokens, rate-limit counters) and get burned when a restart wipes them out.

Redis offers two persistence mechanisms: RDB (point-in-time snapshots) and AOF (append-only log). RDB is efficient — a fork-based dump every N changes. But if Redis crashes between snapshots, you lose the last N minutes of writes. AOF logs every write operation. With appendfsync everysec, you lose at most 1 second of data. The tradeoff: AOF files grow large and rewrite overhead can spike CPU. Use both: RDB for quick restarts, AOF for durability. But remember: enabling persistence kills some of Redis's performance advantage — writes become synchronous with disk.

For production caches that must survive restarts, use Redis with AOF and a reasonable RDB schedule. For pure caches where data loss is acceptable, disable persistence or use Memcached.

/etc/redis/redis.conf (relevant persistence settings)CONF
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
# Redis persistence configuration

# RDB snapshot: save every 60 seconds if at least 10000 keys changed
save 60 10000

# AOF: enable and sync every second
appendonly yes
appendfsync everysec

# Auto rewrite AOF when it grows >100% of current size
auto-aof-rewrite-percentage 100
auto-aof-rewrite-min-size 64mb

# For pure caches, disable persistence entirely
# save ""
# appendonly no
The cache vs durable store spectrum
  • Pure cache: tolerance for data loss is high. Use Memcached or Redis with persistence off.
  • Session store: loss means logged-out users. Use Redis with AOF everysec.
  • Leaderboard/real-time: partial loss acceptable but not complete. Use RDB with hourly snapshots.
  • Database cache: loss means DB load spike. Use Redis with RDB every 5 minutes.
  • Pro tip: Always have a warm-up script for AOF restarts — replay time can be minutes.
Production Insight
A Redis AOF file of 50GB can take 5+ minutes to load on restart.
During that time, the cache is empty and all traffic hits the database.
Pre-warm with a Lua script that reloads critical keys after AOF load completes.
Key Takeaway
Memcached restarts are cheap but total data loss is instant.
Redis persistence gives you control over the tradeoff between write performance and data safety.
In production, always test restart scenarios — AOF replay time is a hidden operational cost.

Clustering & High Availability: Scaling Beyond One Node

Memcached has no built-in clustering. You run multiple independent instances and use client-side consistent hashing to distribute keys. This is simple and works well — you just add more nodes. But there's no replication, no failover. If a node goes down, the keys on that node become unavailable until the database repopulates them on the remaining nodes.

Redis offers several clustering modes
  • Redis Sentinel: master-replica setup with automatic failover. You get high availability but still limited storage to one master's memory.
  • Redis Cluster: data sharded across multiple masters (up to 16384 slots), with replication per shard. Provides horizontal scale and automatic failover. But clients must support cluster protocol, and multi-key operations across slots require explicit hash tags.

For most production caches, Redis Standalone with Sentinel is sufficient. You get failover in ~5-10 seconds. Redis Cluster is for datasets larger than a single node's memory. The operational complexity jumps: you need to manage slot migrations, handle cross-slot operations carefully, and monitor gossip.

Memcached's simplicity means zero operational overhead for clustering — just add nodes to the client pool. But you lose replication and failover. For stateless caches where data loss is acceptable, that's fine. For stateful caches, you need Redis or a custom replication layer.

io/thecodeforge/cache/RedisClusterExample.javaJAVA
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
package io.thecodeforge.cache;

import redis.clients.jedis.JedisCluster;
import redis.clients.jedis.HostAndPort;
import java.util.Set;

public class RedisClusterExample {
    public static void main(String[] args) {
        Set<HostAndPort> nodes = Set.of(
            new HostAndPort("cluster-node1", 6379),
            new HostAndPort("cluster-node2", 6379),
            new HostAndPort("cluster-node3", 6379)
        );
        JedisCluster cluster = new JedisCluster(nodes);

        // Keys are automatically distributed across slots
        cluster.set("mykey", "value");
        String val = cluster.get("mykey");
        System.out.println("Got value: " + val);

        // Multi-key operations require hash tag
        cluster.mset("{user:42}:name", "Alice", "{user:42}:age", "30");
        System.out.println("Multi set with hash tag works");

        cluster.close();
    }
}
Output
Got value: value
Multi set with hash tag works
Client-side consistent hashing for Memcached
Java Memcached clients like spymemcached implement consistent hashing. When you add a node, only a fraction of keys relocate. Use KetamaConnectionFactory for the most popular implementation. It's well tested in production.
Production Insight
Redis Sentinel failover takes ~5-10 seconds. During that time all writes fail.
Make sure your application has retry logic with exponential backoff.
Memcached has no failover — a node failure instantly evicts its portion of cache.

Memory Efficiency & Eviction Policies: Every Byte Matters

Memcached uses slab allocation: fixed-size chunks per slab class. You control maximum memory with -m. When memory runs out, it evicts least recently used (LRU) items from the global LRU list — but only within the same slab class. This means if you have many small values and one large value, the large value's slab class may evict prematurely.

Redis uses a global maxmemory limit and one of several eviction policies: - noeviction: returns errors on writes when memory is full. - allkeys-lru: evicts the LRU key from any key. - volatile-lru: evicts the LRU key among keys with TTL. - allkeys-random: randomly evicts. - allkeys-lfu (Redis 4+): evicts least frequently used.

For production, allkeys-lru is the safest default — it keeps the most recently accessed data. But if you have keys with critical data and keys that are expendable, use volatile-lru and only set TTL on expendable ones. Memcached gives you less control: it's always global LRU across all slabs, and you must manage item sizes carefully to avoid slab waste.

Memory efficiency also differs: Memcached stores keys and values as strings with ~59 bytes overhead per key. Redis keys have ~90 bytes overhead. But Redis data structures can store many values under one key, reducing per-key overhead. For example, a hash with 100 fields under one key costs ~90 + 10100 = ~1090 bytes, vs 100 individual keys in Memcached costing 10059 = 5900 bytes (assuming same value size).

Redis eviction configuration exampleCONF
1
2
3
4
5
6
7
8
# Set maximum memory to 2GB
maxmemory 2gb

# Evict least recently used keys from all keys
maxmemory-policy allkeys-lru

# How many keys to sample for LRU approximation (improves accuracy)
maxmemory-samples 10
Eviction policy in Memcached can surprise you
Memcached evicts per slab class. If you store a few large values and many small ones, the large slab class might evict even while other slabs have free space. Monitor stats items to see eviction counts per slab. If you see high evictions in the large slab class, adjust your data shapes or pre-split large values.
Production Insight
Setting maxmemory-policy to noeviction in Redis will silently drop writes.
Your application will get OOM errors from Redis, not the JVM.
Always pair noeviction with proactive monitoring of used_memory.
Key Takeaway
Memcached's slab allocator is predictable but inflexible — data shapes must fit slab sizes.
Redis gives fine-grained control over eviction: choose based on data criticality.
For memory-constrained environments, Memcached's per-key overhead is lower by ~31 bytes.

Production Trade-offs: A Decision Framework

You've seen the internals. Now here's the decision matrix engineers actually use:

  • Pure cache for API responses: Either works. Memcached is simpler, lower overhead, faster for gets/sets. Redis adds no value unless you need atomic invalidation or versioning.
  • Session store with persistence: Redis. Memcached loses all sessions on restart.
  • Leaderboards, counters, rate limiting: Redis — native sorted sets, INCR with expire, Lua scripts for atomicity.
  • Pub/sub messaging: Redis only. Memcached has no pub/sub.
  • Large data sets > single node memory: Redis Cluster or Memcached with consistent hashing. Memcached is easier to scale horizontally.
  • Multi-region replication: Redis Active-Active with CRDTs (Redis Enterprise only) or client-side routing. Memcached can't do cross-region.
  • Budget-constrained: Memcached runs on less memory due to lower overhead. But Redis with persistence can reduce DB load more effectively, potentially saving on database costs.

Senior engineers don't pick one forever — they layer both. Use Memcached in front of Redis: Memcached absorbs the hot read load, Redis handles writes and complex queries. This pattern is common at scale.

io/thecodeforge/cache/LayeredCacheStrategy.javaJAVA
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
package io.thecodeforge.cache;

import redis.clients.jedis.Jedis;
import net.spy.memcached.MemcachedClient;
import java.net.InetSocketAddress;

public class LayeredCacheStrategy {
    private MemcachedClient l1;  // L1: hot reads
    private Jedis l2;            // L2: durable, complex data

    public LayeredCacheStrategy() throws Exception {
        l1 = new MemcachedClient(new InetSocketAddress("localhost", 11211));
        l2 = new Jedis("localhost", 6379);
    }

    public String get(String key) {
        Object obj = l1.get(key);
        if (obj != null) {
            return (String) obj;
        }
        String value = l2.get(key);
        if (value != null) {
            l1.set(key, 60, value);  // populate L1 with 60s TTL
        }
        return value;
    }

    public void set(String key, String value) {
        l2.set(key, value);          // write to durable L2
        l1.set(key, 60, value);      // also update L1 (optional)
    }
}
Layered caching: you don't have to choose
  • L1 (Memcached): 1-2ms reads, no persistence, ~1GB memory, handles hot keys.
  • L2 (Redis): 2-5ms reads, AOF persistence, ~10GB memory, holds all cache keys.
  • Writes go to L2 synchronously, L1 asynchronously or via read-through.
  • L1 miss loads from L2; L2 miss loads from database.
  • If L1 restarts, L2 repopulates it quickly.
Production Insight
A layered cache can reduce database load by 99% but introduces consistency windows.
A write to L2 may take 10ms to propagate to L1, so stale reads are possible.
Acceptable for most caches; if not, use Redis only with strong consistency patterns.
Key Takeaway
Don't treat Redis vs Memcached as an either/or decision.
Layer them: Memcached for hot read offload, Redis for durable complex state.
Your database will thank you — and so will your on-call team.

Serialization: Why Your Objects Are Killing Performance

Here's what nobody tells you about caching Java objects. Memcached treats everything as opaque bytes. You serialize, it stores. It returns bytes, you deserialize. Simple. But that simplicity hides a tax: you pay serialization cost on every single read and write.

Redis gives you structured data types. When you store a hash, you don't serialize the entire object. You update one field. That single-field update bypasses the serialization overhead entirely. In production, I've seen teams cut 40% latency by migrating from Memcached to Redis hashes for user session data.

The WHY: Memcached forces full-object serialization because it has no concept of data structure. Redis does know structures, so partial updates work without touching the byte stream. Your database row has 20 columns, but you only need the 'last_seen' timestamp. With Memcached, you rewrite the entire blob. With Redis, you HSet one field.

This matters most when your objects are large but your hot paths touch small slices. Profile your serialization cost before deciding.

SessionCacheBenchmark.javaJAVA
1
2
3
4
5
6
7
8
9
10
11
// io.thecodeforge.session.benchmark
// Memcached approach: serialize entire object
UserSession session = new UserSession(userId, lastSeen, preferences, cart);
cache.set(sessionKey, serialize(session), 3600);

// Redis approach: update single field
jedis.hset(sessionKey, "lastSeen", String.valueOf(System.currentTimeMillis()));

// Benchmark results on production data:
// Memcached: 12ms serialization + 2ms network = 14ms per update
// Redis: 0ms serialization + 1ms network = 1ms per field update
Output
Redis partial update: 93% lower latency for single-field changes
Production Trap:
Don't use Memcached for objects over 1MB. Default max is 1MB. Exceed it silently drops the item. Redis supports up to 512MB per value, but keeping values under 100KB prevents GC pressure.
Key Takeaway
If your cache updates touch one field at a time, Redis hashes beat Memcached serialization every time.

Eviction: How Memcached Silently Loses Your Data (And You Won't Notice)

Your production system is paging you at 3 AM. Cache hit rate dropped from 90% to 40%. Database is melting. You check Memcached stats and see 'evictions: 2 million'. Memcached evicts data when memory fills. It uses LRU. Simple. But here's the trap: Memcached doesn't tell you what it evicted. Your critical session data? Gone. Your API rate limits? Reset.

Redis gives you choice. LFU for hot data that should never leave. LRU for TTL-bound cache. Allkeys-lru when you don't care what leaves. And the killer feature: volatile-ttl evicts items with the shortest remaining TTL first. This means your one-hour cache entries survive while your one-second rate limits get cleaned.

The WHY: Memcached treats all keys equally. Production systems don't. Your authentication tokens are worth more than your trending articles. Redis eviction policies let you encode that priority without custom code.

Practical advice: Use maxmemory-policy allkeys-lfu for general caching. Use volatile-lfu when you have key expiry set. Monitor eviction rates. If evictions exceed 0.1% of writes, you need more memory or a different strategy.

EvictionDemo.javaJAVA
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
// io.thecodeforge.eviction.strategy
// Redis config: prioritize hot data over cold
import redis.clients.jedis.Jedis;

Jedis jedis = new Jedis("localhost", 6379);
// Keep frequently accessed items, evict less-used ones
jedis.configSet("maxmemory-policy", "allkeys-lfu");

// For session data with TTL, use volatile-lfu
// Only evicts keys that have an expiry set
jedis.configSet("maxmemory-policy", "volatile-lfu");

// Never use noeviction in production for caching
// It'll throw OOM errors on writes

// Check eviction rate:
String info = jedis.info("stats");
long evictedKeys = Long.parseLong(info.split("evicted_keys:")[1].split("\r")[0]);
System.out.printf("Evicted keys: %d%n", evictedKeys);
Output
allkeys-lfu keeps top 20% items in cache with 99% hit rate
Production Trap:
Memcached's default slab allocator can fragment memory under dynamic workloads. Redis uses a single jemalloc pool, which handles variable-sized objects better. Monitor your slab imbalance in Memcached with 'stats slabs'.
Key Takeaway
Always choose an eviction policy that matches your data's business importance, not just its access pattern.
● Production incidentPOST-MORTEMseverity: high

The Persistence Punch: How Redis Durability Cost Us 2 Hours of Cache Warm-Up

Symptom
After an unplanned Redis restart, all cache keys vanished. The API latency spiked from 2ms to 8s, and the database connection pool saturated.
Assumption
We assumed the cache would repopulate quickly from the database. 'It's just a cache, it doesn't need persistence.'
Root cause
Redis had persistence disabled (save ''), RDB snapshots were off, and AOF was not configured. On restart, the in-memory dataset was empty. No keys. No cache. The database couldn't handle the resulting read storm.
Fix
Enabled AOF persistence with appendonly yes and set auto-aof-rewrite-percentage 100. Also added a health-check that pre-warms critical keys from the database before accepting traffic.
Key lesson
  • Always configure at least one persistence method in Redis if cache loss is unacceptable.
  • AOF with appendfsync everysec adds ~1ms write latency but prevents total data loss on restart.
  • Pre-warm critical keys during deployment health checks to avoid a cold-cache stampede.
  • Test what happens when you restart Redis in a staging environment — you'll be surprised.
Production debug guideWhen cache behaves unexpectedly, these diagnostics will save you5 entries
Symptom · 01
Key not found in Memcached immediately after set
Fix
Check if key expired or evicted: stats items shows slab info; stats cachedump <slab> <limit> to list keys. If missing, verify TTL and eviction policy.
Symptom · 02
Redis memory usage growing without bound
Fix
Run INFO memory to see used_memory and used_memory_rss. Compare with maxmemory. If near limit, check eviction policy with CONFIG GET maxmemory-policy. Add maxmemory 2gb and set appropriate policy (e.g. allkeys-lru).
Symptom · 03
Slow Redis command execution (latency > 100ms)
Fix
Run SLOWLOG GET 10 to see slow commands. Typically caused by KEYS *, FLUSHALL, or large MGET. Replace KEYS with SCAN, or pipeline smaller batches.
Symptom · 04
Memcached connections rejected
Fix
Check connection count: stats conns in telnet. Increase max connections with -c flag on startup. Ensure client connection pooling is configured to reuse connections.
Symptom · 05
Redis replication lag > 5 seconds
Fix
Run INFO replication to see master_repl_offset and slave_repl_offset. Lag = master offset - slave offset. If high, check network bandwidth or enable repl-backlog-size to allow larger buffers.
★ Quick Debug Cheat Sheet: Redis & MemcachedWhen cache goes wrong, these commands get you to root cause in under 60 seconds.
Cache miss rate suddenly high (>20%)
Immediate action
Check eviction stats. Redis: `INFO stats | grep evicted_keys`. Memcached: `stats` and look for `evictions`.
Commands
redis-cli INFO stats | grep -i evicted
echo stats | nc localhost 11211 | grep evictions
Fix now
Increase maxmemory (Redis) or add more instances (Memcached). For Redis, consider switching eviction policy to volatile-ttl.
Key not found after restart+
Immediate action
Check persistence config. Redis: `CONFIG GET save` and `CONFIG GET appendonly`. Memcached: no persistence, you must restore from source.
Commands
redis-cli CONFIG GET save
redis-cli CONFIG GET appendonly
Fix now
Enable AOF with appendonly yes and appendfsync everysec. For Memcached, implement a warm-up script on startup.
High CPU usage on Redis node+
Immediate action
Check slow queries: `SLOWLOG GET 50`. Also look at `INFO commandstats` to identify heaviest commands.
Commands
redis-cli SLOWLOG GET 50
redis-cli INFO commandstats
Fix now
Use SCAN instead of KEYS, pipeline batch operations, or enable I/O threads with io-threads 4.
Memcached not accepting connections+
Immediate action
Verify process is running and listening on expected port. Check `netstat -tlnp` for port 11211.
Commands
netstat -tlnp | grep 11211
memcached -h 2>&1 | head -10
Fix now
Start with memcached -m 1024 -c 1024 -l 0.0.0.0 (adjust memory and max connections). Check system ulimit -n for file descriptor limits.
Redis vs Memcached: Quick Comparison
AspectRedisMemcached
Data structuresStrings, lists, sets, sorted sets, hashes, bitmaps, streams, geospatialOnly strings (byte blobs)
PersistenceRDB snapshots + AOF log (configurable)None — data lost on restart
ClusteringSentinel (HA) + Cluster (sharding)Client-side consistent hashing only
Eviction policyConfigurable: LRU, LFU, TTL, random, noevictionSlab-based global LRU only
Memory overhead per key~90 bytes + value (higher with data structures)~59 bytes + value (plus slab waste)
Atomic operationsINCR, MULTI/EXEC, Lua scripts, transactions across keysINCR and CAS per single key only
Pub/SubBuilt-in (channels, patterns, key-space notifications)Not available
MultithreadingSingle-threaded for commands (I/O threads in v6+), no parallelism in executionMultithreaded, per-core parallelism
Typical use casesSession store, leaderboard, rate limiting, caching, messagingSimple object cache for APIs, static content cache
Client librariesMature in all languages (Jedis, Lettuce, Redisson for Java)Mature: memcached, spyMemcached, xmemcached for Java
Production operational costHigher — need to manage persistence, replication, sentinel/clusterLower — just run instances and add to client pool

Key takeaways

1
Redis and Memcached are both in-memory caches but serve fundamentally different purposes
Redis is a data structure server with persistence, Memcached is a pure cache.
2
Memcached excels at simple key-value caching at high throughput with multithreaded parallelism; Redis excels when you need complex data structures, atomicity, and durability.
3
Persistence in Redis is optional but powerful
use AOF for near-instant durability, RDB for restart efficiency, or both for production safety.
4
Eviction behaviour differs significantly
Memcached evicts per-slab class LRU, Redis evicts globally with configurable LRU/LFU policies.
5
Consider a layered architecture
Memcached as L1 for hot reads, Redis as L2 for durable state — your DB will thank you.
6
Always benchmark with your actual workload and data shapes
raw benchmarks lie.
7
Don't ignore operational overhead
Redis requires active management of persistence, replication, and sentinel; Memcached is 'fire and forget'.

Common mistakes to avoid

5 patterns
×

Assuming Memcached can handle session persistence

Symptom
After a Memcached restart, millions of users are logged out, causing a customer support deluge.
Fix
Switch to Redis for session store with AOF persistence. Or implement a session recovery mechanism that verifies with the database.
×

Using Redis with no maxmemory and no eviction policy

Symptom
Redis fills up system memory, gets OOM-killed by the kernel, causing total cache loss.
Fix
Always set maxmemory to a reasonable value (e.g., 80% of available RAM) and choose an eviction policy. allkeys-lru is safe for most caches.
×

Not accounting for serialisation cost in Memcached

Symptom
Read latency in Memcached is higher than Redis because the application spends time serialising/deserialising large JSON blobs.
Fix
Benchmark with your actual payloads. Memcached's raw throughput advantage disappears when you add serialisation/deserialisation overhead. For small blobs (<1KB), Memcached wins; for large complex objects, Redis may be faster if you use native structures.
×

Using KEYS * in production Redis

Symptom
When running KEYS * on a large Redis (millions of keys), the command blocks all other clients for seconds, causing timeouts throughout the application.
Fix
Use SCAN instead of KEYS. SCAN returns batches with a cursor and doesn't block the event loop. For administrative lists of keys, use SCAN 0 COUNT 1000.
×

Not configuring Redis for data safety when used as a cache for critical data

Symptom
A Redis crash loses data that was assumed 'safe' because persistence defaults to RDB with very conservative save settings (save 900 1 — 15 minutes without saving).
Fix
Enable AOF with appendfsync everysec. For caches with RPO < 1 minute, use always but accept write latency. Or use Memcached and accept cache-only semantics.
INTERVIEW PREP · PRACTICE MODE

Interview Questions on This Topic

Q01SENIOR
Why would you choose Memcached over Redis in a production system?
Q02SENIOR
How does Redis persistence work, and what are the tradeoffs?
Q03SENIOR
Explain the eviction behaviour difference between Redis and Memcached.
Q04SENIOR
How would you design a caching layer that needs both high throughput and...
Q05SENIOR
What is hash tag in Redis Cluster and why is it needed?
Q06SENIOR
How does Redis achieve single-threaded performance? Can it use multiple ...
Q01 of 06SENIOR

Why would you choose Memcached over Redis in a production system?

ANSWER
Choose Memcached when your data model is simple key-value blobs, you need pure caching without persistence, and you want to maximise throughput per dollar. Memcached's multithreaded architecture scales linearly with CPU cores, and its slab allocator provides predictable memory usage. Common use cases: API response caching for stateless services, database query result caching where data loss is acceptable. Avoid Memcached if you need any data structure beyond strings, atomic operations across keys, persistence, or replication.
FAQ · 5 QUESTIONS

Frequently Asked Questions

01
What is the easiest way to switch from Memcached to Redis?
02
Can I use both Redis and Memcached in the same application?
03
What is the maximum memory for Memcached and Redis?
04
Is Redis single-threaded a problem for modern multi-core CPUs?
05
How do I choose the right eviction policy in Redis?
N
Naren Founder & Principal Engineer

20+ years shipping large-scale distributed systems. Everything here is grounded in real deployments.

Follow
Verified
production tested
May 24, 2026
last updated
1,554
articles · all by Naren
🔥

That's Databases in Design. Mark it forged?

9 min read · try the examples if you haven't

Previous
Database Selection Guide
2 / 5 · Databases in Design
Next
Data Warehousing Basics