0Pricing
Spring Boot 4 Complete Guide · Pelajaran

Penyimpanan Cache dalam Memori dengan Penyetelan Caffeine

Konfigurasikan penghapusan, kedaluwarsa, dan kebijakan ukuran Caffeine untuk cache lokal berthroughput tinggi.

Penyimpanan Cache dalam Memori dengan Penyetelan Caffeine adalah pelajaran Spring Boot 4 Complete Guide gratis di CoddyKit. Ini adalah pelajaran 2 dari 4. Kamu bisa membaca pelajaran lengkapnya di bawah secara gratis — lalu praktikkan langsung di browser dengan editor kode bawaan dan tutor AI 24/7. Ini adalah bagian dari jalur belajar Spring Boot 4 Complete Guide, dan progresmu tersinkronisasi di web dan aplikasi CoddyKit. Kursus Spring Boot 4 Complete Guide mencakup 4 pelajaran total.

Bagian dari pelajaran ini belum diterjemahkan dan ditampilkan dalam bahasa Inggris.

Why Caffeine for Local Caches

Caffeine is a high-performance, near-optimal Java caching library and the default in-memory cache for Spring Boot when it is on the classpath.

  • It uses the Window TinyLFU eviction policy, which beats plain LRU on real-world hit rates.
  • It supports size-based, time-based, and reference-based eviction.
  • It is fully concurrent and lock-free on the read path, ideal for high-throughput services.

In this lesson you will tune Caffeine's eviction, expiry, and size policies so a local cache stays fast without exhausting heap.

Adding Caffeine to a Spring Boot 4 App

Spring Boot auto-configures Caffeine when both spring-boot-starter-cache and the caffeine dependency are present.

Then enable caching with @EnableCaching on a configuration class. Methods annotated with @Cacheable will use the Caffeine-backed CacheManager.

import org.springframework.cache.annotation.EnableCaching;
import org.springframework.context.annotation.Configuration;

@Configuration
@EnableCaching
public class CacheConfig {
    // CaffeineCacheManager is auto-configured
    // when com.github.ben-manes.caffeine:caffeine is on the classpath
}

Maximum Size Eviction

The most common policy for a local cache is bounded size. Use maximumSize to cap the number of entries; Caffeine evicts the least valuable entries (Window TinyLFU) once the bound is exceeded.

  • Pick a size that fits comfortably in heap given your value object footprint.
  • Eviction is not immediate at the boundary; it happens promptly but asynchronously.
import com.github.benmanes.caffeine.cache.Caffeine;
import org.springframework.cache.caffeine.CaffeineCacheManager;
import org.springframework.context.annotation.Bean;

@Bean
public CaffeineCacheManager cacheManager() {
    CaffeineCacheManager manager = new CaffeineCacheManager("products");
    manager.setCaffeine(Caffeine.newBuilder()
            .maximumSize(10_000)
            .recordStats());
    return manager;
}

Weight-Based Eviction

When entries vary wildly in size, bound the cache by weight instead of count. Provide a maximumWeight and a Weigher that returns each entry's cost.

  • You cannot combine maximumSize and maximumWeight on the same cache.
  • Weights are computed once at insertion and are not updated afterward.
import com.github.benmanes.caffeine.cache.Caffeine;

Caffeine.newBuilder()
        .maximumWeight(50_000_000) // ~50MB budget
        .weigher((String key, byte[] value) -> value.length)
        .build();

expireAfterWrite vs expireAfterAccess

Time-based expiry comes in two flavors:

  • expireAfterWrite: entry expires a fixed duration after it was created or last replaced. Best for data with a known freshness window (e.g. a price valid for 5 minutes).
  • expireAfterAccess: entry expires a duration after its last read or write. Best for keeping hot data alive and dropping idle entries.

You may combine both; the entry expires when either condition fires first.

import com.github.benmanes.caffeine.cache.Caffeine;
import java.time.Duration;

Caffeine.newBuilder()
        .maximumSize(10_000)
        .expireAfterWrite(Duration.ofMinutes(5))
        .expireAfterAccess(Duration.ofMinutes(2))
        .build();

Configuring via application.properties

For a single shared spec, Spring Boot lets you skip Java config and use the spring.cache.caffeine.spec property. It accepts the comma-separated Caffeine spec string.

This is convenient but applies the same spec to every cache name; per-cache tuning still requires a programmatic CaffeineCacheManager or a custom CacheLoader.

spring.cache.type=caffeine
spring.cache.cache-names=products,prices
spring.cache.caffeine.spec=maximumSize=10000,expireAfterWrite=5m,recordStats

refreshAfterWrite for Stale-While-Revalidate

refreshAfterWrite differs from expiry: instead of removing the entry, it asynchronously reloads it after the duration while still serving the old value. This avoids a latency spike on the first request after staleness.

  • It requires a LoadingCache (a cache built with a CacheLoader).
  • Only one thread triggers the refresh; others keep reading the existing value.
  • Combine with a longer expireAfterWrite as a hard ceiling.
import com.github.benmanes.caffeine.cache.Caffeine;
import com.github.benmanes.caffeine.cache.LoadingCache;
import java.time.Duration;

LoadingCache<String, String> cache = Caffeine.newBuilder()
        .refreshAfterWrite(Duration.ofMinutes(1))
        .expireAfterWrite(Duration.ofMinutes(10))
        .build(key -> loadFromDatabase(key));

A Standalone Caffeine Demo

Here is a complete, framework-free program demonstrating maximumSize eviction. Insert more entries than the bound and observe that the cache never exceeds its size after cleanup.

This is the kind of micro-benchmark you can run to validate a tuning choice before wiring it into Spring.

import com.github.benmanes.caffeine.cache.Cache;
import com.github.benmanes.caffeine.cache.Caffeine;

public class CaffeineDemo {
    public static void main(String[] args) {
        Cache<Integer, String> cache = Caffeine.newBuilder()
                .maximumSize(3)
                .build();

        for (int i = 0; i < 10; i++) {
            cache.put(i, "value-" + i);
        }
        cache.cleanUp(); // force pending eviction work

        System.out.println("Estimated size: " + cache.estimatedSize());
        System.out.println("Get key 9: " + cache.getIfPresent(9));
    }
}

Per-Cache Tuning with Custom Specs

Real services need different policies per cache: a tiny hot lookup table vs a large warm dataset. Subclass or configure CaffeineCacheManager so each name gets its own builder.

One clean approach is registering individual native caches by name on the manager.

import com.github.benmanes.caffeine.cache.Caffeine;
import org.springframework.cache.caffeine.CaffeineCacheManager;
import java.time.Duration;

@Bean
public CaffeineCacheManager cacheManager() {
    CaffeineCacheManager manager = new CaffeineCacheManager();
    manager.registerCustomCache("prices", Caffeine.newBuilder()
            .maximumSize(1_000)
            .expireAfterWrite(Duration.ofSeconds(30))
            .build());
    manager.registerCustomCache("catalog", Caffeine.newBuilder()
            .maximumSize(100_000)
            .expireAfterAccess(Duration.ofHours(1))
            .build());
    return manager;
}

Measuring Hit Rate with recordStats

You cannot tune what you do not measure. Enable recordStats() to expose CacheStats: hit count, miss count, eviction count, and average load penalty.

  • A low hit rate suggests the cache is too small or the keys too cardinal.
  • High eviction with high miss rate means maximumSize is starving the working set.
  • Spring Boot's Micrometer integration publishes these as metrics when stats are on.
import com.github.benmanes.caffeine.cache.Cache;
import com.github.benmanes.caffeine.cache.Caffeine;
import com.github.benmanes.caffeine.cache.stats.CacheStats;

public class StatsDemo {
    public static void main(String[] args) {
        Cache<String, Integer> cache = Caffeine.newBuilder()
                .maximumSize(2)
                .recordStats()
                .build();

        cache.put("a", 1);
        cache.getIfPresent("a"); // hit
        cache.getIfPresent("b"); // miss

        CacheStats stats = cache.stats();
        System.out.printf("hits=%d misses=%d rate=%.2f%n",
                stats.hitCount(), stats.missCount(), stats.hitRate());
    }
}

Avoiding Common Tuning Pitfalls

A few traps to watch for under high throughput:

  • Unbounded caches: never build a cache without a size or expiry bound, or you risk an OutOfMemoryError.
  • initialCapacity: set it near the expected steady-state size to avoid resize churn on the read-heavy path.
  • Soft/weak references: softValues() ties eviction to GC pressure, which is unpredictable; prefer explicit size/time bounds for latency-sensitive caches.
  • refreshAfterWrite without a loader: it silently has no effect on a manual cache.

Quick Check: Choosing an Expiry Policy

You cache product prices that the upstream system guarantees are valid for exactly 5 minutes after publication. Reads are frequent but you must never serve a price older than 5 minutes. Which Caffeine policy fits best?

Recap

You tuned Caffeine for high-throughput local caching in Spring Boot 4:

  • Size: maximumSize for uniform entries, maximumWeight + Weigher for variable cost.
  • Time: expireAfterWrite for freshness contracts, expireAfterAccess to keep hot data, combine for both ceilings.
  • Refresh: refreshAfterWrite on a LoadingCache for stale-while-revalidate without latency spikes.
  • Config: spring.cache.caffeine.spec for one shared spec, or registerCustomCache for per-cache tuning.
  • Measure: always enable recordStats() and watch hit rate and eviction count to guide further tuning.

Pertanyaan yang Sering Diajukan

Apakah pelajaran “Penyimpanan Cache dalam Memori dengan Penyetelan Caffeine” gratis?

Ya — teks lengkap “Penyimpanan Cache dalam Memori dengan Penyetelan Caffeine” gratis dibaca di sini di web. Untuk praktiknya secara interaktif (editor kode bawaan dan tutor AI 24/7) dan buka sisa kursus Spring Boot 4 Complete Guide, upgrade ke CoddyKit PRO. Kursus Spring Boot 4 Complete Guide mencakup 4 pelajaran total.

Apa yang akan aku pelajari di “Penyimpanan Cache dalam Memori dengan Penyetelan Caffeine”?

Konfigurasikan penghapusan, kedaluwarsa, dan kebijakan ukuran Caffeine untuk cache lokal berthroughput tinggi. Kamu berlatih Spring Boot 4 Complete Guide dengan kode praktik yang langsung kamu jalankan di browser, dan tutor AI 24/7 menjawab pertanyaanmu saat kamu mengerjakan pelajaran ini.

Apakah aku perlu pengalaman untuk memulai Spring Boot 4 Complete Guide?

Tidak diperlukan pengalaman sebelumnya. Spring Boot 4 Complete Guide di CoddyKit dirancang untuk pemula hingga pelajar tingkat lanjut, jadi kamu bisa memulai di sini atau dari awal dan belajar sesuai kecepatan kamu sendiri. Ini adalah pelajaran 2 dari 4.

Berapa lama pelajaran “Penyimpanan Cache dalam Memori dengan Penyetelan Caffeine” memakan waktu?

Sebagian besar pelajaran CoddyKit memakan waktu sekitar 5–10 menit. Setiap pelajaran ringkas dan interaktif, jadi kamu membuat kemajuan stabil dan melanjutkan dari tempat kamu tinggalkan di web dan aplikasi.

Bisakah aku menulis dan menjalankan kode dalam pelajaran Spring Boot 4 Complete Guide ini?

Ya. Setiap pelajaran Spring Boot 4 Complete Guide menyertakan editor kode bawaan, jadi kamu menulis dan menjalankan kode nyata langsung di browser dan mendapatkan umpan balik AI instan — tidak diperlukan penyiapan lokal.

Semua pelajaran dalam kursus ini

  1. Dasar-Dasar Abstraksi Cache Spring
  2. Penyimpanan Cache dalam Memori dengan Penyetelan Caffeine
  3. Penyimpanan Cache Terdistribusi dengan Redis dan TTL
  4. Cache Stampede, Invalidasi, dan Konsistensi
← Kembali ke Spring Boot 4 Complete Guide