# Redis vs Memcached

## 1. The Question This Topic Actually Answers

Both tools solve "keep hot data in RAM so I don't hit the database every request." The interesting question isn't "which is faster" (for a plain get/set of a small string, both are sub-millisecond and the difference is noise) — it's **which mechanical design decisions each one made, and which real workloads those decisions favor**. `01-redis/cache_redis_theory.md` and `02-memcached/cache_memcached_theory.md` each cover one side in depth; this file is the head-to-head.

## 2. Side-by-Side

| | Redis | Memcached |
|---|---|---|
| Value types | strings, hashes, lists, sets, sorted sets, streams | opaque byte strings only |
| Execution model | single-threaded event loop | fixed multi-threaded pool, shared locked hash table |
| Max value size | 512MB (string) | 1MB default |
| Persistence | RDB snapshots and/or AOF log | none — restart empties everything |
| Replication / clustering | built-in (Redis Sentinel, Redis Cluster) | none server-side; client-side sharding only |
| Concurrency-safe compound ops | `MULTI`/`EXEC`, `WATCH`, and atomic type-specific commands (`HINCRBY`, `ZADD`, ...) | `cas`/`gets`, `incr`/`decr` — narrower primitive set |
| Pub/Sub or messaging | Pub/Sub (fire-and-forget) + Streams (durable, replayable) | none |
| Eviction policy | selectable (`allkeys-lru`, `allkeys-lfu`, `volatile-ttl`, `noeviction`, ...) | fixed strict LRU per slab class, not configurable |
| Memory allocation | generic allocator (jemalloc by default) | slab allocation with fixed chunk-size classes |
| Multi-core scaling (single instance) | needs Redis Cluster / multiple instances to use more cores | one process uses multiple cores natively via its thread pool |
| Typical role | cache **and/or** primary data store / broker / queue backend | cache only |

## 3. Choosing Based on the Actual Workload

**Favor Memcached when:**
- The workload is purely simple key -> flat-blob caching (rendered HTML fragments, serialized API responses) with no need for server-side structure operations.
- You want to maximize raw throughput per box for that narrow workload by using every CPU core in one process.
- You explicitly do NOT want persistence — an empty-on-restart cache is a correctness *feature* here, not a gap, because you never want stale-but-durable data surviving a deploy.

**Favor Redis when:**
- You need atomic operations on structured data server-side (increment one hash field, push onto a capped list, maintain a leaderboard) without round-tripping the whole blob to the client.
- You want the cache to survive a restart without a full cold-cache thundering herd (see `05-cache_invalidation/cache_cache_invalidation_theory.md`), via RDB/AOF.
- You need pub/sub, streams, rate-limiting primitives, distributed locks (`SETNX`/`SET ... NX`), or geo commands — none of which have a Memcached equivalent.
- You want built-in replication/clustering rather than building your own client-side sharding layer.

## 4. What They Genuinely Share

Both are volatile-by-assumption caches sitting in front of a slower source of truth; both expect the application to handle cache misses by querying that source of truth (this is the cache-aside pattern covered fully in `06-cache_aside_write_through_write_behind/`); both rely on TTL for automatic staleness bounding (`07-ttl/`); and both, at multi-node scale, depend on consistent hashing to route a given key to a specific node predictably (`08-distributed_caching/`).

## 5. Under the Hood: Why the Architectures Diverged, Mechanically

This is the crux, not a footnote. Memcached's single-type, no-persistence, no-replication design is not an *earlier, less mature version* of what Redis became — it is a deliberately narrower system, and the narrowness is what buys its concurrency model.

Because a Memcached value is an opaque blob with exactly one operation shape ("replace these bytes" / "read these bytes," plus `incr`/`decr` for the special case of a numeric string), multiple threads can service unrelated keys with only fine-grained per-bucket locking — there is no cross-key invariant a lock has to protect beyond "don't corrupt this one hash bucket while it's being mutated." A `get key1` and a `set key2` can execute genuinely concurrently on different cores with only brief mutual exclusion, if any, on the shared structures they each touch.

Redis's data model breaks that independence. A single logical operation like `ZADD` has to correctly update a sorted set's dual backing structure (skip list *and* hash table, kept consistent with each other) as one atomic step, and richer future commands can combine multiple keys (`SINTERSTORE`, `MULTI`/`EXEC` blocks touching several keys) with a correctness requirement that nothing else runs in between. Redis's authors chose to buy that correctness for free — no lock design, no risk of forgetting a lock, no lock contention overhead — by making the *entire command execution path* single-threaded, accepting the throughput ceiling of one core for command execution in exchange. Memcached's authors chose to buy raw multi-core throughput by keeping the data model simple enough that locking could stay cheap and local.

Neither choice is a mistake the other project corrected; they are two different answers to the same trade-off (concurrency-model complexity vs. data-model richness), each internally consistent with what the tool is for.

![Same workload, two architectures: Redis serializes everything through one thread for free atomicity; Memcached parallelizes across threads because its data model needs only local locking](diagrams/01-architecture-contrast.svg)

## 6. Common Pitfalls

1. **Assuming one is a strict upgrade of the other** — picking Redis "because it can do more" for a workload that's genuinely just flat-blob caching adds operational complexity (persistence config, richer failure modes) for no benefit.
2. **Running Memcached but architecting as if it persists** — after any restart/crash/OOM-kill, the cache is empty; code paths must tolerate a 100% cache-miss cold start.
3. **Running single-instance Redis and expecting Memcached-like multi-core throughput** — a single Redis process caps command execution at one core; horizontal scaling requires Redis Cluster or multiple instances, not just "more Redis on the same box."
4. **Comparing raw benchmark numbers out of context** — for trivial get/set of small values, both are sub-millisecond; meaningful differences only show up under specific access patterns (many small independent keys favoring Memcached's parallelism, or compound structured operations favoring Redis's atomics).
5. **Forgetting Memcached has no selectable eviction policy** — if you need `volatile-ttl` or `allkeys-lfu` semantics, that requirement alone rules out Memcached.

## 7. Quick Reference

```
                  Redis                          Memcached
Data types        strings, hash, list, set,      strings only (opaque bytes)
                  zset, stream
Concurrency       single-threaded event loop      thread pool + fine-grained locks
Persistence       RDB and/or AOF                  none
Replication       built-in (Sentinel/Cluster)      none (client-side sharding only)
Eviction          selectable policy                fixed strict LRU per slab class
Best for          structured ops, durability,      pure flat-blob caching,
                  pub/sub, rate limiting            max per-box throughput
```
