00:00

What is Caching?

Caching is a technique for keeping a copy of frequently used data in a fast, temporary storage area so it can be read again quickly. Instead of fetching the same data again and again from a slow source, such as a database or an external service, the application keeps a ready copy close by, usually in memory. This makes responses faster and reduces the work done by the slower system behind it.

In simple words, caching means saving data for quick reuse. Just as you save a phone number in your contacts instead of searching for it every time, applications cache data to avoid repeating the same expensive work.

1. Why Is Caching Important?

  • Faster applications: reading from memory is far quicker than querying a database or calling another service.
  • Less load on the database: repeated reads are answered by the cache, so the database can handle more important work.
  • Better user experience: pages and APIs respond quickly, even during busy periods.
  • Lower cost: fewer database queries and external API calls can mean smaller servers and lower bills.
  • Higher throughput: each request finishes sooner, so the system can serve more users per second.
  • Resilience: a cache can keep serving popular data for a short time even if the source is slow.

The speed difference is large. These are rough, typical numbers to show the scale:

Where the Data Comes FromTypical Time
Application memory (local cache)Well under a microsecond
Distributed cache such as Redis (same data centre)About 0.5–1 millisecond
Simple indexed database queryA few milliseconds
Complex database query or external API callTens to hundreds of milliseconds

2. Key Caching Terms

TermMeaning
Cache hitThe requested data is found in the cache.
Cache missThe data is not in the cache, so it must be loaded from the source.
Hit ratioHits divided by total requests. 90% means 9 of every 10 reads came from the cache.
TTL (Time To Live)How long an entry stays valid before it expires.
EvictionRemoving entries to make room when the cache is full.
InvalidationRemoving or updating an entry because the original data changed.
Stale dataA cached copy that is older than the current data in the source.
Cold / warm cacheA cold cache is empty (many misses); a warm cache already holds popular data.

3. How Caching Works, Step by Step

  1. A user requests some data, such as an article.
  2. The application first checks whether the article is in the cache.
  3. If it is (a cache hit), the data is returned immediately.
  4. If it is not (a cache miss), the data is fetched from the database and saved in the cache.
  5. Future requests for the same article are served from the cache until it expires or changes.
Cache hit and cache miss
 Request ---> Application ---> Cache: is the data here?
                                   |
              +--------------------+--------------------+
              | YES (cache hit)                         | NO (cache miss)
              v                                         v
       Return cached data                   Read from database
                                                        |
                                                        v
                                            Save a copy in cache
                                                        |
                                                        v
                                               Return the data

4. Real-Life and Technical Examples

Real-Life Example

Imagine you visit a restaurant every day and order the same dish. On the first day the chef prepares everything from scratch, which takes time. After that, the chef keeps the sauce ready because many customers order it, so your dish arrives faster. The ready sauce is like a cache: prepared once, reused many times.

Technical Example: A Blog Website

Suppose a blog website loads each article from the database. If thousands of users open the same popular article, the database runs the same query thousands of times and may become overloaded. With caching:

  • The article is fetched from the database once.
  • A copy is stored in the cache (memory).
  • The next users get the article directly from the cache.
  • When the author edits the article, the cached copy is removed so readers see the new version.

If the article is read 10,000 times an hour and the cache hit ratio is 99%, the database handles only about 100 of those reads instead of 10,000.

5. Types of Caching

Caches exist at many layers between the user and the database:

TypeWhere It LivesWhat It StoresExamples
Browser cacheUser's deviceImages, CSS, JavaScript, some API responsesHTTP Cache-Control headers
CDN cacheServers near the userStatic files, images, videosCloudFront, Cloudflare
Reverse proxy cacheIn front of the app serversFull HTTP responsesNGINX, Varnish
In-memory (local) cacheInside each application instanceObjects used often by that instanceCaffeine, ConcurrentHashMap
Distributed cacheSeparate cache servers shared by all instancesSessions, query results, objectsRedis, Memcached, Hazelcast
Database cacheInside the database engineRecently used data pages and query plansPostgreSQL shared buffers, MySQL buffer pool

Local vs distributed: a local cache is the fastest but each server has its own copy, so copies can differ between servers. A distributed cache is slightly slower (a network call) but all servers share the same data. Many systems use both: a small local cache in front of a shared Redis cache. Static content is usually best served from a CDN.

6. Caching Strategies

A caching strategy decides who loads data into the cache and when writes reach the database.

StrategyHow It WorksProsCons
Cache-aside (lazy loading)The application checks the cache; on a miss it reads the database and fills the cache.Simple; only requested data is cachedFirst request is slow; data can become stale
Read-throughThe application always asks the cache; the cache itself loads missing data from the database.Cleaner application codeNeeds a cache library or provider that supports loading
Write-throughEvery write goes to the cache and the database at the same time.Cache is always up to dateWrites are slower; may cache data nobody reads
Write-behind (write-back)Writes go to the cache first; the cache saves them to the database later.Very fast writesRisk of losing data if the cache fails before saving
Write-aroundWrites go only to the database; the cache is filled later on reads.Avoids caching data that is rarely readFirst read after a write is a miss

Cache-aside is the most common choice for web applications and is what Spring's @Cacheable does.

7. Cache Eviction Policies

Memory is limited, so when a cache is full it must remove some entries. The eviction policy decides which ones:

  • LRU (Least Recently Used): removes the item that has not been used for the longest time. The most popular general-purpose choice.
  • LFU (Least Frequently Used): removes the item used the fewest times. Good when some items stay popular for a long time.
  • FIFO (First In, First Out): removes the oldest item, regardless of how often it is used.
  • TTL-based expiry: removes items automatically after a fixed time.
  • Random: removes a random item; simple and sometimes surprisingly effective.

8. Cache Invalidation

Keeping cached data correct after the source changes is one of the hardest parts of caching. Common approaches:

  • Expire with a TTL: accept that data may be a little old, and let entries expire after a set time.
  • Evict on write: delete the cached entry whenever the data is updated or deleted, so the next read loads the fresh value.
  • Update on write: write the new value into the cache at the same time as the database.
  • Event-based invalidation: when data changes, publish an event so every service and cache can remove its copy.
  • Versioned keys: include a version in the key (for example article:42:v7) so updated data uses a new key.

9. Common Caching Problems

ProblemWhat HappensCommon Fix
Stale dataUsers see old values after the data changesEvict on write and use sensible TTLs
Cache stampede (thundering herd)A popular entry expires and thousands of requests hit the database at onceLet only one request reload the value; add random variation (jitter) to TTLs
Cache penetrationRepeated requests for data that does not exist always miss and hit the databaseCache "not found" results briefly; validate input; use a Bloom filter
Cache avalancheMany entries expire at the same moment, or the cache server goes downSpread out expiry times; make the cache highly available
Hot keysOne extremely popular key overloads a single cache nodeAdd a small local cache in front; replicate the hot key
Memory pressureThe cache grows until it slows down or crashes the applicationSet a maximum size and an eviction policy

10. Caching in Java

A Simple LRU Cache

Java's LinkedHashMap can keep entries in access order and remove the oldest one automatically, which makes a tiny LRU cache:

LruCache.java
import java.util.LinkedHashMap;
import java.util.Map;

public class LruCache<K, V> extends LinkedHashMap<K, V> {

    private final int capacity;

    public LruCache(int capacity) {
        super(16, 0.75f, true); // true = keep entries in access order, not insertion order
        this.capacity = capacity;
    }

    @Override
    protected boolean removeEldestEntry(Map.Entry<K, V> eldest) {
        return size() > capacity; // remove the least recently used entry when full
    }

    public static void main(String[] args) {
        LruCache<String, String> cache = new LruCache<>(3);
        cache.put("A", "Apple");
        cache.put("B", "Banana");
        cache.put("C", "Cherry");

        cache.get("A");          // A is now the most recently used
        cache.put("D", "Date");  // cache is full, so B (least recently used) is removed

        System.out.println(cache.keySet());
    }
}
Output
[C, A, D]

This version is not thread-safe. For real applications, use a proven library such as Caffeine.

Cache-Aside with Expiry (TTL)

This small cache loads data on a miss and keeps it for a fixed time:

TtlCache.java
import java.time.Duration;
import java.util.Map;
import java.util.concurrent.ConcurrentHashMap;
import java.util.function.Function;

public class TtlCache<K, V> {

    private record Entry<V>(V value, long expiresAt) {
    }

    private final Map<K, Entry<V>> store = new ConcurrentHashMap<>();
    private final long ttlMillis;

    public TtlCache(Duration ttl) {
        this.ttlMillis = ttl.toMillis();
    }

    public V getOrLoad(K key, Function<K, V> loader) {
        long now = System.currentTimeMillis();
        Entry<V> entry = store.get(key);

        if (entry != null && entry.expiresAt() > now) {
            System.out.println("Cache HIT  for " + key);
            return entry.value();
        }

        System.out.println("Cache MISS for " + key + " -> loading from database");
        V value = loader.apply(key);
        store.put(key, new Entry<>(value, now + ttlMillis));
        return value;
    }

    public void evict(K key) {
        store.remove(key); // call this when the source data changes
    }

    public static void main(String[] args) throws InterruptedException {
        TtlCache<Long, String> articles = new TtlCache<>(Duration.ofSeconds(1));
        Function<Long, String> database = id -> "Article #" + id;

        articles.getOrLoad(42L, database); // miss: first request
        articles.getOrLoad(42L, database); // hit: served from memory
        Thread.sleep(1_100);               // wait until the entry expires
        articles.getOrLoad(42L, database); // miss: entry expired
        articles.evict(42L);               // the article was edited
        articles.getOrLoad(42L, database); // miss: entry was evicted
    }
}
Output
Cache MISS for 42 -> loading from database
Cache HIT  for 42
Cache MISS for 42 -> loading from database
Cache MISS for 42 -> loading from database

Caching in Spring Boot with Redis

Spring's cache annotations implement cache-aside for you. Add spring-boot-starter-cache and spring-boot-starter-data-redis, then configure Redis:

application.yml
spring:
  cache:
    type: redis
    redis:
      time-to-live: 10m          # every cached entry expires after 10 minutes
      cache-null-values: false
  data:
    redis:
      host: localhost
      port: 6379
ArticleService.java
package com.javabytechie.blog;

import org.springframework.cache.annotation.CacheEvict;
import org.springframework.cache.annotation.CachePut;
import org.springframework.cache.annotation.Cacheable;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

@Service
public class ArticleService {

    private final ArticleRepository articleRepository;

    public ArticleService(ArticleRepository articleRepository) {
        this.articleRepository = articleRepository;
    }

    @Cacheable(cacheNames = "articles", key = "#id")
    public Article getArticle(Long id) {
        // Runs only on a cache miss; the result is stored in the "articles" cache.
        return articleRepository.findById(id)
                .orElseThrow(() -> new ArticleNotFoundException(id));
    }

    @Transactional
    @CachePut(cacheNames = "articles", key = "#result.id")
    public Article updateArticle(Article article) {
        // Saves to the database and writes the new version into the cache.
        return articleRepository.save(article);
    }

    @Transactional
    @CacheEvict(cacheNames = "articles", key = "#id")
    public void deleteArticle(Long id) {
        // Removes the cached copy so nobody reads a deleted article.
        articleRepository.deleteById(id);
    }
}
BlogApplication.java
package com.javabytechie.blog;

import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.cache.annotation.EnableCaching;

@SpringBootApplication
@EnableCaching // turns on @Cacheable, @CachePut and @CacheEvict
public class BlogApplication {

    public static void main(String[] args) {
        SpringApplication.run(BlogApplication.class, args);
    }
}

By default, Redis stores cached objects using Java serialization, so Article must implement Serializable, or you can configure a JSON serializer. The annotations only work when the method is called from another bean, because Spring applies caching through a proxy.

11. When to Use and When to Avoid Caching

Use Caching When DataAvoid Caching When Data
Is read much more often than it is writtenChanges very frequently
Does not change very oftenMust always be exactly real-time, such as an account balance during payment
Is slow or expensive to load or calculateCould cause wrong results or decisions if slightly out of date
Is requested by many users, such as popular productsIs unique per request and rarely reused
Can safely be a little out of dateIs sensitive and must not be copied to shared storage

12. Best Practices

  1. Cache data that is read often, changes rarely and is costly to fetch.
  2. Always set a TTL, even a long one, so wrong data cannot live forever.
  3. Set a maximum size and choose an eviction policy, usually LRU.
  4. Evict or update cache entries whenever the source data changes.
  5. Use clear, consistent key names, such as article:42.
  6. Add small random variation to TTLs to avoid many entries expiring together.
  7. Protect popular entries from cache stampedes by letting one request reload them.
  8. Treat the cache as optional: the application must still work if the cache is empty or down.
  9. Monitor hit ratio, memory use and eviction counts.
  10. Never cache secrets or personal data in shared caches without encryption and access control.

13. Summary

Caching is a powerful technique that improves application performance by keeping frequently used data in fast, temporary storage. Caches exist at many levels, from the browser and CDN to in-memory and distributed caches such as Redis. Strategies like cache-aside, write-through and write-behind decide how data enters the cache, while eviction policies like LRU decide what leaves it. The hardest part is keeping cached data correct, which is handled with TTLs and invalidation. Used wisely, caching makes applications faster, reduces database load and helps systems scale.