Caching is one of the most impactful performance levers in any system. But "add a cache" is not a strategy — it is a starting point. The value comes from choosing the right caching pattern: the specific contract between your application, the cache, and the database that determines how data flows through the system.
This post covers five foundational caching patterns. Each pattern makes different tradeoffs around consistency, performance, resilience, and complexity. Understanding these tradeoffs is what separates a cache that quietly does its job from one that quietly serves stale data in production.
1. Cache-Aside
In the Cache-Aside pattern, the application orchestrates all data flow. The cache is treated as an independent, standalone data store — the application interacts with both the cache and the database directly and is fully responsible for keeping the cache populated.
How CRUD Operations Work
Read: The application checks the cache first. On a cache hit, data is returned immediately — the database is never touched. On a cache miss, the application queries the database, writes the result into the cache (typically with a TTL), and returns the data. This is a "lazy loading" approach: data enters the cache only when it is actually requested.
Create: New records are written directly to the database. The cache is bypassed entirely. The new entry only enters the cache when a subsequent read triggers a cache miss and lazily loads it.
Update: The application writes the updated data to the database, then deletes the corresponding cache key. Deletion is preferred over updating the cache because it avoids race conditions when multiple concurrent requests try to update the same key out of order. The next read fetches the fresh value from the database.
Delete: The application deletes the record from the database and immediately evicts the corresponding cache key.
Pros
- Resilient to cache failures. If the cache goes down, the application falls back to the database. Performance degrades, but the system stays up.
- Memory efficient. The cache only holds data that is actively being requested.
- Data model independence. Cache structures do not need to map 1:1 to database tables — the application controls the shape.
Cons
- Cache miss penalty. The first request for any piece of data incurs a triple-trip cost: cache lookup, database read, cache write.
- Stale data risk. If a database update succeeds but the cache eviction fails, the application serves outdated data until the TTL expires.
2. Read-Through
In the Read-Through pattern, the cache becomes the primary data interface. The application queries the cache exclusively — it never talks to the database directly for reads. The cache itself is configured with internal logic or a data provider plugin that knows how to fetch from the underlying database on a miss.
How CRUD Operations Work
Read: The application queries the cache. On a hit, data is returned directly. On a miss, the cache (not the application) automatically intercepts the miss, queries the database, stores the result, and returns it to the application. The application code has no awareness of the fallback — it always gets data from the cache layer.
Create / Update / Delete: Read-Through is strictly a reading strategy. Write operations are handled by a paired write pattern — most commonly Write-Through or Write-Around. If paired with Write-Through, the application writes to the cache and the cache synchronously persists to the database. If paired with Write-Around, writes go directly to the database, and new data enters the cache only on the next read miss.
Pros
- Clean code architecture. The application code has no fallback routines or conditional database queries. Separation of concerns is enforced by the pattern itself.
- Read scalability. The cache intercepts all read misses, shielding the database from spike loads.
Cons
- Infrastructure complexity. Requires a caching provider or custom plugin that supports database integration drivers.
- Tight schema coupling. Database schema changes often require updates to the cache layer's translation logic.
3. Write-Back (Write-Behind)
In the Write-Back pattern, the application writes exclusively to the cache, which acknowledges the operation immediately. The cache then persists updates to the database asynchronously in the background — either after a set interval or in batches.
How CRUD Operations Work
Read: Because updates are written to the cache first, the cache holds the most recent data — even before it reaches the database. Reads are fast and rarely stale for active data. On a cache miss (e.g., data evicted due to size limits or expired TTL), the data is fetched from the database and loaded into the cache.
Create: The record is written directly into the cache, which immediately returns success. At a later time, the cache framework groups new records and executes a bulk insert into the database.
Update: The change is written to the cache, which marks the entry as "dirty" (indicating the cache version is newer than the database version) and returns success. A background daemon periodically sweeps for dirty entries, batches them, and pushes an asynchronous update to the database.
Delete: The cache marks the item as deleted and immediately returns success. In the background, deletion requests are aggregated and issued as a batch delete against the database.
Pros
- Extreme write performance. Writing to RAM is orders of magnitude faster than writing to disk. Write-heavy applications see massive throughput gains.
- Reduced database stress. Batching individual writes into bulk operations drops database utilization and prevents connection pool bottlenecks.
- Resilience to DB spikes. If the database experiences a brief outage or spike, the cache absorbs writes and queues them.
Cons
- Risk of data loss. If the cache crashes while holding dirty entries that have not synced to the database, that data is permanently gone.
- Complex cache management. Requires robust queue handling, retry logic for failed background writes, and careful eviction strategies to avoid losing un-synced entries.
4. Write-Around
In the Write-Around pattern, the cache is a strict read-only optimization layer. All writes bypass the cache entirely and go directly to the database. The cache is only populated when a subsequent read results in a cache miss.
How CRUD Operations Work
Read: Functions identically to Cache-Aside or Read-Through. The application checks the cache; on a miss, it fetches from the database, populates the cache, and returns the data.
Create: New entries go directly to the database. The cache is completely ignored. The record enters the cache only when a user explicitly reads it later — hence the name, data circles "around" the cache.
Update: The application writes the update directly to the database and evicts the corresponding cache key. The next read will pull the fresh value from the database and repopulate the cache.
Delete: The application deletes the record from the database and evicts the cache key.
When to Use
Write-Around is the standard choice for systems with a high volume of writes that are rarely or never read again. Real-time analytics ingestion, event logging, and audit trail systems are classic examples — data is written at high frequency, but most of it is never accessed individually after ingestion.
Pros
- Zero cache pollution. Write-heavy data that is never read again does not waste cache memory.
- Strong database consistency. Writes go straight to the database with no intermediate asynchronous layer, minimizing race conditions and data loss risk compared to Write-Back.
Cons
- Higher read latency for recent writes. Data written moments ago is guaranteed to be a cache miss on the first read, since it was never cached.
- Cache miss storms. If a burst of reads targets recently written data, the database absorbs the full load until the cache is populated.
5. Write-Through
In the Write-Through pattern, the cache sits directly in the write path. When data is created, updated, or deleted, the application writes to the cache, and the cache immediately writes the same data to the database. The operation is synchronous — the application only receives a success confirmation after both the cache and the database have been updated.
How CRUD Operations Work
Read: Because every write updates the cache, reads almost always result in a cache hit. On the rare miss, the application fetches from the database and populates the cache.
Create: The application writes the new record to the cache. The cache immediately persists it to the database. Only after both stores confirm does the application receive a success response.
Update: The application writes the modification to the cache, which synchronously commits it to the database. Both stores are updated in lockstep — the cache is never out of sync with the database.
Delete: The application issues a delete to the cache, which removes the entry from memory and concurrently deletes it from the database.
Pros
- Absolute data consistency. The cache and database are always in sync. There is no window where the cache holds stale or newer-than-database data.
- No data loss risk. Unlike Write-Back, data is fully persisted to disk-based storage before the application proceeds.
- Simplified cache invalidation. There is no need for TTL-based expiration or manual eviction strategies — the cache is always current.
Cons
- Write latency. Every write operation pays the cost of two sequential writes (cache + database). For write-heavy workloads, this can become a bottleneck.
- Cache pollution. Every piece of written data enters the cache, including data that may never be read. This consumes memory with potentially low-value entries.
Comparison: All 5 Patterns at a Glance
| Dimension | Cache-Aside | Read-Through | Write-Back | Write-Around | Write-Through |
|---|---|---|---|---|---|
| Consistency | Moderate | Moderate | Low | High | Very High |
| Write perf | Fast | Varies | Fastest | Fast | Slowest |
| Read perf | Fast | Fast | Very fast | Fast | Very fast |
| Data loss risk | None | None | High | None | None |
| Memory usage | Efficient | Efficient | High | Efficient | High |
| Complexity | Low | Medium | High | Low | Medium |
Pattern-by-Pattern Summary
Cache-Aside — General purpose. App manages cache and DB independently. Best for read-heavy workloads with tolerance for occasional staleness.
Read-Through — Cache auto-fetches on miss. Clean separation of concerns, but requires a cache provider with DB integration drivers.
Write-Back — Fastest writes (RAM only, async DB sync). Best for write-heavy workloads, but dirty entries are lost if the cache crashes.
Write-Around — Writes bypass the cache entirely. Best when written data is rarely read again (event logs, analytics ingestion).
Write-Through — Synchronous write to both cache and DB. Strictest consistency, but every write pays double latency.
Choosing the Right Pattern
There is no universally correct caching pattern. The right choice depends on your system's read/write ratio, consistency requirements, tolerance for data loss, and operational complexity budget.
Most production systems combine multiple patterns. A common pairing is Read-Through + Write-Around for systems that ingest data at high volume but serve reads from a curated subset. Another is Read-Through + Write-Through when consistency is non-negotiable and the write volume is manageable.
The patterns described here are not mutually exclusive — they are building blocks. Understanding how each one handles the data flow between your application, cache, and database is what lets you compose them into a caching strategy that fits your actual workload.