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.
- 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
requirepassand TLS for secure access
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.
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.
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).
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.
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 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.
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).
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.
| Aspect | Redis | Memcached |
|---|---|---|
| Data structures | Strings, lists, sets, sorted sets, hashes, bitmaps, streams, geospatial | Only strings (byte blobs) |
| Persistence | RDB snapshots + AOF log (configurable) | None — data lost on restart |
| Clustering | Sentinel (HA) + Cluster (sharding) | Client-side consistent hashing only |
| Eviction policy | Configurable: LRU, LFU, TTL, random, noeviction | Slab-based global LRU only |
| Memory overhead per key | ~90 bytes + value (higher with data structures) | ~59 bytes + value (plus slab waste) |
| Atomic operations | INCR, MULTI/EXEC, Lua scripts, transactions across keys | INCR and CAS per single key only |
| Pub/Sub | Built-in (channels, patterns, key-space notifications) | Not available |
| Multithreading | Single-threaded for commands (I/O threads in v6+), no parallelism in execution | Multithreaded, per-core parallelism |
| Typical use cases | Session store, leaderboard, rate limiting, caching, messaging | Simple object cache for APIs, static content cache |
| Client libraries | Mature in all languages (Jedis, Lettuce, Redisson for Java) | Mature: memcached, spyMemcached, xmemcached for Java |
| Production operational cost | Higher — need to manage persistence, replication, sentinel/cluster | Lower — just run instances and add to client pool |
Key Takeaways
- 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.
- Memcached excels at simple key-value caching at high throughput with multithreaded parallelism; Redis excels when you need complex data structures, atomicity, and durability.
- Persistence in Redis is optional but powerful: use AOF for near-instant durability, RDB for restart efficiency, or both for production safety.
- Eviction behaviour differs significantly: Memcached evicts per-slab class LRU, Redis evicts globally with configurable LRU/LFU policies.
- Consider a layered architecture: Memcached as L1 for hot reads, Redis as L2 for durable state — your DB will thank you.
- Always benchmark with your actual workload and data shapes — raw benchmarks lie.
- Don't ignore operational overhead: Redis requires active management of persistence, replication, and sentinel; Memcached is 'fire and forget'.
Common Mistakes to Avoid
- 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 setmaxmemoryto a reasonable value (e.g., 80% of available RAM) and choose an eviction policy.allkeys-lruis 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, usealwaysbut accept write latency. Or use Memcached and accept cache-only semantics.
Interview Questions on This Topic
- QWhy would you choose Memcached over Redis in a production system?Mid-levelReveal
- QHow does Redis persistence work, and what are the tradeoffs?SeniorReveal
- QExplain the eviction behaviour difference between Redis and Memcached.SeniorReveal
- QHow would you design a caching layer that needs both high throughput and data durability?SeniorReveal
- QWhat is hash tag in Redis Cluster and why is it needed?Mid-levelReveal
- QHow does Redis achieve single-threaded performance? Can it use multiple cores?Mid-levelReveal
Frequently Asked Questions
What is the easiest way to switch from Memcached to Redis?
If your Memcached usage is purely key-value blobs, you can switch to Redis with minimal code changes. Most Redis clients support setting values with TTL: redis.set("key", "value") instead of memcached.set("key", 3600, "value"). You'll also gain INCR, multip operations, and TTL flexibility. No data migration needed — just point your cache client to Redis. If you use Memcached for session storage, Redis is a drop-in replacement and adds persistence.
Can I use both Redis and Memcached in the same application?
Absolutely — and many production systems do. The common pattern is layered caching: Memcached as L1 (fast, volatile, hot reads) and Redis as L2 (durable, complex operations). Writes go to Redis, and Memcached is populated on read-through. This gives you the best of both: Memcached's throughput for reads, Redis's durability and data structures for writes.
What is the maximum memory for Memcached and Redis?
Memcached can use all available system memory by default, limited by the -m flag (in MB). There's no built-in limit on number of keys — only memory. Redis has a maxmemory setting (in bytes). Without it, Redis will keep allocating until the OOM killer steps in. For production, always set maxmemory to something like 80% of available RAM. Both can scale horizontally: Memcached via client-side consistent hashing, Redis via Cluster or Sentinel.
Is Redis single-threaded a problem for modern multi-core CPUs?
For most cache workloads, Redis single-threaded is fast enough (100k+ ops/s per instance). If you need more throughput, use Redis Cluster to shard across multiple nodes (each node uses a core). Or use Memcached which uses all cores. For latency-sensitive applications with many concurrent connections, Redis I/O threads (v6+) help with network handling, but command execution remains single-threaded. The bottleneck is often network bandwidth, not CPU — Redis runs up to 10 Gbps line rate just fine.
How do I choose the right eviction policy in Redis?
Start with allkeys-lru — it's the safest default for general caching. It evicts the least recently used key from any key, so frequently accessed data stays. If some keys are critical and others are expendable, use volatile-lru and set TTL only on expendable keys. allkeys-lfu is good when access frequency matters more than recency, e.g., for hot key detection. Avoid noeviction unless you monitor used_memory closely — it will reject writes when memory is full.
That's Databases in Design. Mark it forged?
6 min read · try the examples if you haven't