Caching in Java: Redis, Memcached, and Built-in Solutions: from A to Z
Caching is one of the key tools for improving the performance of Java applications. It reduces response time, lowers the load on databases and external APIs, and improves the user experience. In this article, we will cover three main approaches to caching in Java: built-in solutions (ConcurrentHashMap, Caffeine, Ehcache), distributed systems (Redis), and lightweight network caches (Memcached). You will learn when and what to choose, and get ready-made code examples.
What is caching and why is it needed in Java?
Caching is the temporary storage of frequently requested data in a fast storage medium (usually RAM). In Java, this is especially relevant for:
- Reducing latency — data from the cache is returned in microseconds, not milliseconds (as from a database).
- Reducing resource load — databases, external services, and disks work less.
- Saving resources — fewer CPU and I/O operations.
Key cache metrics: hit ratio (percentage of successful accesses) and TTL (time-to-live for an entry).
Built-in Solutions: from Simple to Advanced
For small projects or single-threaded scenarios, simple data structures can be used. However, for industrial applications, specialized libraries are better suited.
1. ConcurrentHashMap — basic cache
The simplest way is to use ConcurrentHashMap. It is thread-safe but lacks automatic eviction mechanisms (TTL, eviction).
import java.util.concurrent.ConcurrentHashMap;
public class SimpleCache { private final ConcurrentHashMap<String, Object> cache = new ConcurrentHashMap<>();
public void put(String key, Object value) { cache.put(key, value); }
public Object get(String key) { return cache.get(key); }
public void evict(String key) { cache.remove(key); }
public void clear() { cache.clear(); }}Pros: simplicity, zero dependencies.
Cons: no automatic invalidation, manual memory management.
2. Caffeine — modern high-performance cache
The Caffeine library is the de facto standard for in-memory caching in Java. It supports:
- Automatic eviction by time (TTL) and size.
- Asynchronous loading.
- Statistics (hit rate, miss rate).
import com.github.benmanes.caffeine.cache.Cache;import com.github.benmanes.caffeine.cache.Caffeine;import java.util.concurrent.TimeUnit;
public class CaffeineExample { public static void main(String[] args) { Cache<String, String> cache = Caffeine.newBuilder() .expireAfterWrite(10, TimeUnit.MINUTES) .maximumSize(1000) .recordStats() // for collecting statistics .build();
// Adding cache.put("user:1", "Alice");
// Retrieval with automatic loading String value = cache.get("user:2", key -> loadFromDB(key)); System.out.println(value); }
private static String loadFromDB(String key) { // Simulating loading from DB return "Bob"; }}Pros: high performance, flexibility, small footprint (about 600 KB).
Cons: local only (not distributed).
3. Ehcache — powerful in-process cache
Ehcache is a mature library (part of Hibernate). It supports:
- Disk storage (persistence).
- Clustering (via Terracotta).