7 Common Cache Pitfalls and How to Avoid Them

This article outlines seven typical cache problems—including cache penetration, breakdown, avalanche, large keys, hot keys, hit‑rate issues, and data inconsistency—and provides practical solutions such as parameter validation, Bloom filters, null‑value caching, locking, auto‑renewal, random expiration, high‑availability setups, compression, and cache warming to improve system reliability and performance.

Architect
Architect
Architect
7 Common Cache Pitfalls and How to Avoid Them

Introduction

Caching is widely used to reduce database load and improve system performance, but improper usage can lead to many pitfalls. This article summarizes seven common cache problems and offers practical solutions.

1. Cache Penetration

When a request queries a non‑existent ID, both the cache and the database return nothing, causing every request to hit the database. This can overload the database.

1.1 Parameter Validation

Validate request parameters (e.g., ensure user IDs start with a specific prefix) and reject malformed requests early.

1.2 Bloom Filter

Load all existing keys into a Bloom filter stored in memory. When a request arrives, hash the key; if all bits are 1, the key likely exists and the request proceeds to the cache. If any bit is 0, the key definitely does not exist and the request can be rejected.

1.3 Cache Null Values

If a key is missing in both cache and database, store a null placeholder in the cache. Subsequent requests will hit the cache and return the null quickly, avoiding repeated database lookups.

2. Cache Breakdown (Stampede)

When a hot key expires, a surge of requests may simultaneously query the database, potentially crashing it.

2.1 Locking

try {
  String result = jedis.set(productId, requestId, "NX", "PX", expireTime);
  if ("OK".equals(result)) {
    return queryProductFromDbById(productId);
  }
} finally {
  unlock(productId, requestId);
}
return null;

The lock ensures only one request accesses the database for a given product ID at a time.

2.2 Auto‑Renewal

Schedule a job to refresh the key before it expires, e.g., a 30‑minute cache refreshed every 20 minutes.

2.3 Permanent Keys

For certain hot keys (e.g., flash‑sale items), omit expiration and preload them into the cache before the event, deleting them afterward.

3. Cache Avalanche

Multiple hot keys expire simultaneously or the cache server crashes, causing massive database traffic.

3.1 Randomized Expiration

actualExpire = baseExpire + random(1, 60) // seconds

Adding a random offset prevents many keys from expiring at the same moment.

3.2 High Availability

Deploy Redis in Sentinel or cluster mode to avoid single‑node failures.

3.3 Service Degradation

When the cache is down, switch to default fallback data via a global switch that activates after consecutive cache failures.

4. Data Inconsistency Between DB and Cache

Four common write strategies are discussed, each with pros and cons.

4.1 Write Cache → Write DB

If the DB write fails after the cache is updated, the cache holds stale data.

4.2 Write DB → Write Cache

In high‑concurrency scenarios, if the cache write fails after a successful DB write, the cache becomes stale. Also, wrapping both operations in a single transaction can cause deadlocks.

4.3 Delete Cache → Write DB

Deleting the cache before writing the DB can still lead to race conditions where reads obtain stale data.

4.4 Write DB → Delete Cache

If a read occurs before the cache is deleted, it may return outdated data; however, this scenario is less common.

5. Large‑Key Problem

Storing excessively large values (e.g., a category tree with many fields) can degrade performance.

5.1 Reduce Field Names

@AllArgsConstructor
@Data
public class Category {
  @JsonProperty("i") private Long id;
  @JsonProperty("l") private Integer level;
  @JsonProperty("n") private String name;
  @JsonProperty("p") private Long parentId;
  @JsonProperty("c") private List<Category> children;
}

Shortening field names reduces JSON size.

5.2 Data Compression

Compress JSON strings to byte arrays (e.g., using GZip) before storing them in Redis, then decompress on retrieval. This can shrink data size by an order of magnitude.

6. Hot‑Key Problem

Popular keys concentrate traffic on a single Redis node, risking node failure.

6.1 Split Keys

Distribute hot keys across multiple Redis nodes to balance load.

6.2 Add Local Cache

Introduce an in‑process cache layer to offload read traffic, accepting potential consistency trade‑offs.

7. Cache Hit‑Rate Issues

Low hit rates increase database load.

7.1 Cache Warm‑up

Pre‑load essential data into the cache during service startup using background jobs.

7.2 Adjust Expiration Times

Set reasonable TTLs (e.g., seconds to minutes) to keep data fresh without excessive evictions.

7.3 Increase Cache Memory

Allocate more memory to Redis to reduce evictions and improve hit rates.

By applying these techniques, developers can mitigate common cache issues, maintain data consistency, and achieve higher system performance.

Original Source

Signed-in readers can open the original source through BestHub's protected redirect.

Sign in to view source
Republication Notice

This article has been distilled and summarized from source material, then republished for learning and reference. If you believe it infringes your rights, please contactadmin@besthub.devand we will review it promptly.

Distributed Systemsperformancerediscachingcache pitfalls
Architect
Written by

Architect

Professional architect sharing high‑quality architecture insights. Topics include high‑availability, high‑performance, high‑stability architectures, big data, machine learning, Java, system and distributed architecture, AI, and practical large‑scale architecture case studies. Open to ideas‑driven architects who enjoy sharing and learning.

0 followers
Reader feedback

How this landed with the community

Sign in to like

Rate this article

Was this worth your time?

Sign in to rate
Discussion

0 Comments

Thoughtful readers leave field notes, pushback, and hard-won operational detail here.