There are only two hard problems in computer science: cache invalidation and naming things
There are only two hard problems in computer science: cache invalidation and naming things
Lesson outline
Twitter's homepage once required 800 database queries. After adding a Redis caching layer, it required 0 database queries for most requests. That's the power of caching — and why Twitter could serve 500 million tweets per day on commodity hardware.
| Storage Layer | Latency | Throughput | Use Case |
|---|---|---|---|
| CPU L1 Cache | 0.5ns | ∞ | In-process variables |
| RAM (in-process cache) | 100ns | ∞ | Application memory cache |
| Redis (same datacenter) | 0.1–1ms | 100k+ ops/sec | Distributed session, hot data |
| SSD (local) | 150μs | 10k IOPS | Write-through cache |
| Database (network query) | 5–50ms | 1k–10k QPS | Source of truth |
| Database (cross-region) | 50–200ms | 1k QPS | Disaster recovery reads |
The 80/20 Rule of Caching
80% of traffic typically hits 20% of data. Caching that hot 20% eliminates 80% of database load. You don't need to cache everything — just the hot path.
| Pattern | How It Works | Pros | Cons |
|---|---|---|---|
| Cache-Aside (Lazy Loading) | App checks cache first; on miss, loads from DB and populates cache | Only caches what's requested; resilient to cache failure | Cache miss penalty; potential stale data; thundering herd on cold start |
| Write-Through | App writes to cache AND DB synchronously on every write | Cache always has fresh data; no stale reads | Write latency increases; caches data that may never be read |
| Write-Behind (Write-Back) | App writes to cache immediately; DB updated asynchronously | Lowest write latency | Risk of data loss if cache fails before DB write |
| Read-Through | Cache handles DB loading automatically on miss (middleware pattern) | App code simpler; consistent cache population | Less control; requires cache-aware library |
| Refresh-Ahead | Cache proactively refreshes hot data before expiry | No miss penalty for hot keys | Complex; may cache data that becomes stale |
Cache-Aside Is the Production Default
Cache-Aside (Lazy Loading) is the most common production pattern because it's simple, doesn't require special infrastructure, and handles cache failures gracefully (just goes to DB). Use it as your default and only switch to more complex patterns when you have specific needs.
1import Redis from 'ioredis';23const redis = new Redis(process.env.REDIS_URL);45// Cache-Aside Pattern with TTL and error handling6async function getUser(id: string): Promise<User> {7const cacheKey = `user:${id}`;89// 1. Check cache firstCheck cache first — fast path returns immediately without touching DB10const cached = await redis.get(cacheKey);11if (cached) {12return JSON.parse(cached);13}1415// 2. Cache miss — load from database16const user = await db.users.findById(id);17if (!user) throw new Error('User not found');18Always set TTL. Redis with no TTL = memory leak. 1 hour is reasonable for user profiles19// 3. Populate cache with TTL20await redis.setex(cacheKey, 3600, JSON.stringify(user)); // 1 hour TTL2122return user;23}2425// Cache invalidation on updateInvalidate on write — simpler and safer than updating the cached value26async function updateUser(id: string, data: Partial<User>): Promise<User> {27const user = await db.users.update(id, data);2829// Invalidate cache — force next read to go to DB30await redis.del(`user:${id}`);3132return user;33}3435// Thundering herd prevention with distributed lock36async function getUserWithLock(id: string): Promise<User> {37const cacheKey = `user:${id}`;Thundering herd: 1000 requests hit cache miss simultaneously → 1000 DB queries. Lock prevents this38const lockKey = `lock:user:${id}`;3940const cached = await redis.get(cacheKey);41if (cached) return JSON.parse(cached);NX = set only if Not eXists. EX 5 = 5-second lock timeout prevents deadlocks4243// Only one process refills cache at a time44const acquired = await redis.set(lockKey, '1', 'EX', 5, 'NX');4546if (!acquired) {47// Another process is loading — wait briefly and retry48await new Promise(r => setTimeout(r, 50));49return getUserWithLock(id);50}5152try {53const user = await db.users.findById(id);54await redis.setex(cacheKey, 3600, JSON.stringify(user));55return user;56} finally {57await redis.del(lockKey);58}59}
Redis Data Structures for Backend Engineers
Redis Is Not a Database
Redis stores data in RAM. Without RDB/AOF persistence configured, data is lost on restart. Never use Redis as the sole data store for critical data. Always use Redis as a cache or ephemeral store, with a real database as the source of truth.
| Data Type | Acceptable Staleness | Strategy | TTL |
|---|---|---|---|
| User profile | 5 minutes OK | TTL + invalidate on update | 300s |
| Product catalog | 1 hour OK | TTL-based | 3600s |
| User session | Must be live | Short TTL + sliding expiry | 900s (refreshed on activity) |
| Account balance | Never stale | No cache, or write-through with instant invalidation | 0 (no cache) |
| Product inventory | Seconds OK | Short TTL + explicit invalidation on purchase | 30s |
| Leaderboard rankings | Minutes OK | Redis Sorted Set as cache (atomic updates) | 600s |
The Practical Rule
Use TTL-based expiry when stale-for-N-seconds is acceptable. Use explicit deletion for data where stale reads cause correctness issues (financial balances, inventory). Never cache financial data that must be real-time.
Caching questions are almost universal in backend interviews. Demonstrate you understand not just "add Redis" but specific patterns, invalidation strategies, and failure modes.
Common questions:
Strong answers include:
Red flags:
Quick check · Caching Strategies: Redis, Memcached, and Cache Invalidation
1 / 3
Key takeaways
From the books
Designing Data-Intensive Applications — Martin Kleppmann (2017)
Chapter 5: Replication
Caches introduce eventual consistency. Understanding when stale reads are acceptable vs when they cause correctness issues is the key to good caching design.
Ready to see how this works in the cloud?
Switch to Career Paths for structured paths (e.g. Developer, DevOps) and provider-specific lessons.
View role-based pathsSign in to track your progress and mark lessons complete.
Questions? Discuss in the community or start a thread below.
Join DiscordSign in to start or join a thread.