Should I Cache?¶
1. Decision¶
Introduce caching only when read volume significantly exceeds write volume (Read-to-Write ratio > 10:1), latency SLAs cannot be met by database query tuning/indexing alone, or downstream database resources need protection from read thrashing.
2. 30-Second Answer¶
Caching is an optimization strategy, not a default requirement. Introduce a cache when data is read frequently, written infrequently, and tolerant of transient stale reads. Caching trades memory footprint and architectural complexity (cache invalidation, stale reads, dual-write races, cache stampedes) for microsecond read latencies and database CPU offloading. If data changes constantly (Write-heavy), reads are strictly unique per request, or strong transactional consistency is required on every read, adding a cache introduces unnecessary operational overhead and bugs without delivering performance gains.
3. Decision Matrix¶
| Option | Best For | Avoid When | Latency | Scalability | Consistency | Operational Complexity |
|---|---|---|---|---|---|---|
| Add Cache Layer (Redis/Memcached) | High read-to-write ratio (>10:1), slow expensive queries, high traffic static metadata | High write-to-read ratio (<2:1), strict strong read consistency, unique single-use queries | Sub-millisecond (<1ms) | Scale-out read capacity instantly | Eventual (Stale read risks until TTL/invalidation) | High (Cache invalidation, stampede, memory tuning) |
| No Cache (Database Only) | Strong real-time consistency requirements, financial transaction ledgers, write-heavy datasets | Slow complex queries exceeding DB SLAs under load | Low to Medium (2-20ms DB page hits) | Database scale-up; Read Replicas | Strong ACID | Low |
4. Selection Rules¶
| Requirement | Best Choice | Rationale / Example |
|---|---|---|
| Read-Heavy Static Workload (95% Reads) | Add Cache | Product details, user profile static fields, app config flags |
| Write-Heavy / Streaming Telemetry | No Cache | High-velocity IoT sensor metrics, live location tracking updates |
| Expensive Aggregation / Join Queries | Add Cache | Pre-calculated dashboard summary analytics or leaderboard rankings |
| Strict Real-Time Account Balance Verification | No Cache | Reading stale cache state risks overdrafts or double-spending |
| Low-Traffic Application (<50 RPS) | No Cache | Database indexes handle traffic comfortably; cache adds premature complexity |
| Protect Database from Traffic Spikes | Add Cache | Offload peak traffic during flash sales / breaking news events |
5. Tradeoffs¶
- Microsecond Read Latency vs Cache Invalidation Complexity
- Gain: Sub-millisecond responses without touching database disk or CPU.
- Lose: Managing cache invalidation bugs (Phil Karlton: "Hardest problem in CS").
- Database Load Protection vs Memory Cost Overhead
- Gain: Offloads up to 99% of read queries away from primary storage engines.
- Lose: In-memory RAM cost (Redis clusters) adds significant infrastructure billing.
- High System Availability vs Stale Read Risks
- Gain: Serves cached responses even when primary database experiences transient outages.
- Lose: Clients may read outdated data during invalidation lag windows.
- Read Performance Boost vs Write Amplification Penalty
- Gain: Accelerated read throughput for popular hot keys.
- Lose: Every database write now requires invalidating/updating cache keys.
- Architectural Simplicity vs Multi-Tier Failure Modes
- Gain: Scalable sub-millisecond response SLAs.
- Lose: Vulnerable to cache stampedes, penetration, and cold-start DB crashes.
6. Interview Counter Questions¶
- When is adding a cache an architectural anti-pattern?
- What is the Read-to-Write ratio threshold where caching becomes ineffective or detrimental?
- How do you quantify whether to optimize a slow query in the database vs adding a Redis cache?
- What happens to the primary database when a cache node with 99% hit rate suddenly crashes (Cold Cache)?
- What is Cache Penetration and how do Bloom Filters prevent it?
- How do you handle caching when data requires strict row-level authorization per user?
- What is Cache Breakdown vs Cache Avalanche vs Cache Stampede?
- How does caching affect data consistency in a distributed microservices environment?
- When would you choose database read-replicas over an in-memory Redis cache?
- How do you measure Cache Hit Ratio and what is an acceptable production threshold?
7. Gold Standard Answers¶
- Caching is an anti-pattern when applied to write-heavy datasets, unique single-use search queries, strict real-time transactional ledgers, or low-traffic systems where proper database indexing would solve the latency issue cleanly.
- If Read-to-Write ratio is under 3:1, caching adds write invalidation overhead and cache misses dominate, consuming RAM without delivering latency benefits.
- Always optimize DB indexes, query plans, and schema first. If the DB achieves \(<10\text{ms}\) p99 at peak target QPS, omit the cache. Add a cache only when complex joins/aggregations exceed SLA or DB CPU limits are reached.
- Cold Cache failure: Traffic bypasses the dead cache, slamming the database directly with 100% of read traffic, causing database connection pool exhaustion and cascading system outage.
- Cache Penetration occurs when queries search for non-existent keys (e.g., random IDs), bypassing cache and hitting the DB repeatedly. Use a Bloom Filter in front of the cache or cache
NULLvalues with short TTLs. - Either cache raw entity data un-transformed and execute authorization filtering in the application layer, or incorporate tenant/user security scope directly into cache key names (
cache:user:122:document:456). - Cache Breakdown = Single hot key expires \(\rightarrow\) traffic slams DB. Cache Avalanche = Millions of keys expire at the exact same timestamp \(\rightarrow\) DB crashes. Cache Stampede = Concurrent threads compute the exact same expensive missing key simultaneously.
- Caching creates eventual consistency. Microservices reading local or distributed caches will observe state lag relative to the primary database system of record until invalidation completes.
- Choose read-replicas when queries require complex SQL joins, filtering, and pagination over large datasets that don't map cleanly into key-value data structures.
- Measure Cache Hit Ratio = \(\frac{\text{Cache Hits}}{\text{Cache Hits} + \text{Cache Misses}}\). Target \(>90-95\%\) for production read-heavy workloads. A ratio \(<70\%\) indicates wrong key selection or inadequate TTL settings.
8. Common Mistakes¶
- Adding a cache as a band-aid for missing database indexes or poorly written SQL queries.
- Caching rapidly changing state (e.g., live stock ticker or GPS coordinates), forcing non-stop cache invalidations.
- Failing to load-test system behavior when the cache is completely empty or unavailable (Cold Start failure).
- Forgetting to set a TTL on dynamic cache keys, causing Redis memory exhaustion (
OOM). - Caching sensitive user data without security scope in key names, resulting in unauthorized cross-tenant data leakage.
9. Keywords¶
Caching, Read-to-Write Ratio, Cold Cache, Cache Penetration, Bloom Filter, Cache Breakdown, Cache Avalanche, Cache Stampede, Cache Hit Ratio, TTL, Eventual Consistency, Phil Karlton.
10. Cheat Sheet¶
- Golden Rule: Caching is an optimization, not a requirement. Optimize DB indexes first!
- Read-to-Write Ratio: Cache if Read:Write \(> 10:1\). Avoid if Read:Write \(< 3:1\).
- When NOT to Cache: Write-heavy streams, financial transactions, low traffic (<50 RPS), unique ad-hoc queries.
- Cache Hit Target: \(>90-95\%\) hit ratio in production. \(<70\%\) indicates misconfiguration.
- Cold Start Risk: Always implement rate limiting, circuit breakers, and warm-up scripts for cache restarts.
- Security Rule: Include tenant/user ID in cache key strings if data requires authorization boundaries.