Overview

6 Caching

Caching is presented as the final data-focused latency optimization, complementing colocation, replication, and partitioning. It accelerates access by keeping temporary copies of hot data closer to where it’s used, trading freshness and some complexity for speed. Caches may live in memory or persist to disk (reducing cold starts but increasing coordination risk), and their effectiveness hinges on cache hits versus misses, captured by the cache hit ratio. Beyond positive results, negative caching avoids repeating expensive lookups for absent items. Because caches are capacity-limited, they must evict entries, and every choice—persistence, eviction, and freshness—balances latency, storage, and consistency needs. Caching is most compelling for simple key-value access patterns, systems that are hard to modify, or environments constrained by compute and storage.

The chapter surveys read and write strategies and their latency-consistency trade-offs. With cache-aside, applications fetch and populate on misses; with read-through, the cache fetches and can add features like refresh-ahead to hide miss costs. For writes, write-through synchronously updates the backing store (higher latency, stronger sync), whereas write-behind acknowledges quickly and flushes asynchronously (lower latency, weaker durability/consistency). Client-side caching minimizes last-mile latency but complicates transactions and increases memory use; distributed caches introduce partitioning, replication, and coherence concerns akin to replicated systems. Maintaining strict coherence across caches is costly, illustrated by CPU MESI-style protocols—many distributed caches therefore tolerate staleness. Crucially, tail latency remains because misses pay full backing-store cost, so geography and miss handling still matter.

Maximizing cache hit ratio is central: average latency trends toward hit latency as hits rise, so shape access patterns and size caches to the working set. Replacement policies should match workload: LRU favors recency/locality, LFU protects long-term popularity, and FIFO variants like SIEVE combine simplicity with selective reinsertion to avoid evicting hot items. TTL provides simple, time-based invalidation to manage freshness when updates bypass the cache, but choosing durations balances hit ratio against staleness and can cause needless churn if the backing data hasn’t changed. Adjacent techniques further reduce latency: materialized views precompute query results (often with incremental, eventually consistent updates), and memoization caches function outputs to avoid repeated computation. A practical in-application cache for a REST API shows broad latency reductions yet retains a heavy tail—underscoring the need to pair the right strategy, sizing, and freshness controls to the workload while working to lower miss costs.

With cache-aside caching, client first looks up a key from the cache. On cache miss, the clients queries the database and updates the cache.
With read-through caching, client looks up a key from the cache. Unlike with cache-aside caching, the cache queries the database and updates itself on cache miss.
With write-through caching, client writes a key-value pair to the cache. The cache immediately updates cache and the database.
With write-behind caching, client writes a key-value pair to the cache. However, unlike with write-through caching, the cache updates cache but defers the database update. Instead, write-behind cache will batch multiple cache updates to a single database update.
Average cache access latency (y-axis) approaches cache hit latency as the cache hit ratio (x-axis) increase towards 100%.
Cache latency distribution when hit ratio is 80% with cache hit latency of 0.1 seconds and cache miss latency of 1.0 seconds.
Least recently used (LRU) replacement policy evicts values from the cache based on when they were last accessed.
Least frequently used (LFU) replacement policy evicts elements based on how frequently they were used.
First-in, first-out (FIFO) replacement policy evicts elements based on when they were added to the cache. The SIEVE algorithm is a variant of FIFO, which keeps track of cached elements that were accessed to avoid evicting hot elements.
REST API latency eCDF comparing PostgreSQL and PostgreSQL with Moka caching.

Summary

  • Caching is a technique for speeding up data retrieval by storing temporary copies of data closer to where it’s accessed, similar to colocation or replication but with different trade-offs. The primary goal of caching is to reduce the latency and complexity involved in data access.
  • Different caching strategies, cache-aside, read-through, write-through, and write-behind caching, vary in how the cache integrates with the backing store. The various strategies have different trade-offs between latency and complexity because of how they deal with reading and writing.
  • In many applications, you end up using distributed caching, meaning multiple copies of the cache exist. This indicates that you need to deal with cache coherency in the cache unless your application can cope with cache incoherency.
  • Maximizing the hit ratio of your cache is the key to uncovering low-latency access. Different cache replacement policies such as LRU, LFU, and FIFO mandate that entries are evicted from a cache when fully occupied, impacting the hit ratio.
  • Time-to-live (TTL) is a way to deal with data freshness in a cache by specifying how long an entry can be considered valid in a cache until it has to be retrieved again from the backing store. However, TTL is often tricky to get right and can result in a low cache hit ratio if TTL is too low or an application reading stale data if TTL is too high.
  • Materialized views is a technique to speed up database queries by building a copy of the data, organized in views, which can be queried like database tables when constructed.
  • Memoization is a technique to cache the results of computation. Although the term comes from dynamic programming, an everyday use of memoization is to cache the results of REST API calls, which often combine computation and data access.

FAQ

Why cache data, and when is it the right choice?Caching is a good fit when you want lower latency without changing core systems or schemas, and your access patterns are simple. It shines when: - You don’t need multi-record transactions or complex queries. - You can’t modify the existing database/service (for example, no replication available). - You face compute or storage limits that make full replication/colocation impractical. Common wins include user profiles, settings, and session state as key-value pairs. For highly dynamic, complex workloads (recommendations, ad serving), other techniques may be better.
What’s the difference between cache-aside and read-through caching?- Cache-aside: The application looks up the cache; on miss, the app reads from the backing store and then populates the cache. Pros: simple, loosely coupled. Cons: app must coordinate concurrent misses; no transactional integration; tail latency on misses. - Read-through: The cache is active; on miss, the cache fetches from the backing store, updates itself, and returns the value. Pros: transparent to the app; can coordinate misses and offer stronger guarantees; enables refresh-ahead. Cons: tighter coupling to data model and formats; more complex to implement/integrate.
How do write-through and write-behind caching compare?- Write-through: Writes go to the cache and synchronously to the backing store. Pros: keeps cache and store in sync; can be transactional. Cons: write latency ≈ database commit latency; needs data transformation to DB format. - Write-behind: Cache acknowledges quickly and flushes to the backing store asynchronously (often batching). Pros: very low write latency, reduced DB load. Cons: potential inconsistency and data loss if the cache fails before flushing; weaker durability/transactionality. Choose based on your need for write latency vs. durability/consistency.
What is a cache hit ratio, how is it calculated, and why can tail latency still be a problem?The hit ratio is hits divided by total cache lookups (hits + misses). Higher ratios mean more requests served at cache speed. To improve: - Size the cache to at least the working set. - Pick a replacement policy that matches your workload. - Shape access patterns (batch passes) and consider refresh-ahead and negative caching. Even with high hit ratios, miss latency can dominate the tail of the distribution, so reducing miss cost (e.g., locality to the DB, prefetching) matters.
Which cache replacement policy should I use: LRU, LFU, or FIFO/SIEVE?- LRU (Least Recently Used): Good when recent items are likely to be reused (strong temporal locality). Can underperform if accesses have little locality. - LFU (Least Frequently Used): Keeps long-term popular items. Good when “hot” items stay hot over time (e.g., CDN popular assets). - FIFO and SIEVE: FIFO evicts the oldest inserted; SIEVE improves on FIFO by tracking “visited” items to avoid evicting hot entries. These can outperform LRU in some workloads due to simpler, efficient promotion/demotion behavior. Choose based on locality vs. long-term popularity and implementation overhead.
What is TTL and how should I pick a value?Time-to-live limits how long an entry is considered fresh. Pros: simple invalidation without coordinating with the backing store; great when data changes infrequently. Cons: choosing a value is a trade-off—long TTL boosts hit ratio but risks staleness; short TTL keeps data fresh but can hurt hit ratio and add unnecessary misses. For highly dynamic data, combine shorter TTLs with refresh-ahead or event-driven invalidation; for static or slowly changing data, longer TTLs are fine.
What is negative caching and when is it useful?Negative caching stores “no result” outcomes. If a lookup or computation frequently returns empty (e.g., a filter that often finds no matching records), caching the “no result” avoids repeatedly paying the full compute or network cost for the same miss pattern.
What is cache coherence, and how is it different from consistency?Coherence means multiple caches don’t serve stale copies of the same item; updates invalidate or update other copies. Consistency is about the ordering and visibility of operations. You can have coherence without strong consistency. Many distributed application caches choose incoherence (allow stale reads) to avoid coordination overhead; CPU caches often implement coherence protocols because programs expect immediate correctness at that level.
How do client-side and distributed caches affect latency and complexity?- Client-side caching: Puts data in-process or on-device to remove network hops and last-mile latency. Pros: fastest reads/writes (often paired with read-through and write-behind). Cons: higher app memory use; hard to provide transactional guarantees through layers; may need server-side proxies/APIs. - Distributed caching: Multiple cache instances for scale and geography. Requires partitioning, potential replication, and careful thinking about availability, rebalancing, and coherence. It blends the trade-offs of caching, partitioning, and replication.
How do materialized views and memoization relate to caching?- Materialized views: The database stores precomputed query results as a physical object. Some systems refresh incrementally, offering fast reads for complex queries at the cost of additional storage and potentially weaker consistency if maintained outside the DB’s core transaction path. - Memoization: Caches expensive function outputs keyed by inputs. Great for repeated calls with the same arguments (e.g., recursive/dynamic programming, API response caching). Together, they reduce latency—views for data retrieval at scale; memoization for compute-heavy functions.

pro $24.99 per month

  • access to all Manning books, MEAPs, liveVideos, liveProjects, and audiobooks!
  • choose one free eBook per month to keep
  • exclusive 50% discount on all purchases
  • renews monthly, pause or cancel renewal anytime

lite $19.99 per month

  • access to all Manning books, including MEAPs!

team

5, 10 or 20 seats+ for your team - learn more


choose your plan

team

monthly
annual
$49.99
$499.99
only $41.67 per month
  • five seats for your team
  • access to all Manning books, MEAPs, liveVideos, liveProjects, and audiobooks!
  • choose another free product every time you renew
  • choose twelve free products per year
  • exclusive 50% discount on all purchases
  • renews monthly, pause or cancel renewal anytime
  • renews annually, pause or cancel renewal anytime
  • Latency ebook for free
choose your plan

team

monthly
annual
$49.99
$499.99
only $41.67 per month
  • five seats for your team
  • access to all Manning books, MEAPs, liveVideos, liveProjects, and audiobooks!
  • choose another free product every time you renew
  • choose twelve free products per year
  • exclusive 50% discount on all purchases
  • renews monthly, pause or cancel renewal anytime
  • renews annually, pause or cancel renewal anytime
  • Latency ebook for free
choose your plan

team

monthly
annual
$49.99
$499.99
only $41.67 per month
  • five seats for your team
  • access to all Manning books, MEAPs, liveVideos, liveProjects, and audiobooks!
  • choose another free product every time you renew
  • choose twelve free products per year
  • exclusive 50% discount on all purchases
  • renews monthly, pause or cancel renewal anytime
  • renews annually, pause or cancel renewal anytime
  • Latency ebook for free