How to Combine Redis and Guava for Lazy‑Loading Cache in Java
This article explains how to reduce Redis read/write pressure by introducing a Guava local cache for lazy loading, presents design diagrams, provides complete Java code examples for both simple lazy caching and a microservice‑oriented device‑increment cache, and discusses the advantages, disadvantages, and practical considerations of each approach.
Preface
Redis is commonly used as a cache to reduce load on relational databases, but high‑frequency data streams can still overload Redis due to I/O latency. Adding a local cache such as Guava reduces Redis reads and provides flexible expiration policies.
Redis Lazy‑Loading Cache (Query‑then‑Cache)
Data is persisted in MySQL without immediate caching. Only precise queries trigger a cache load, achieving a “query‑then‑cache” pattern.
Flowchart:
Implementation (Spring Boot, RedisTemplate):
public class XxLazyCache {
@Autowired
private RedisTemplate<String, Xx> redisTemplate;
@Autowired
private XxService xxService;
// Query: check cache first, load from DB on miss and populate cache
public Xx getXx(int id) {
Xx cached = getXxFromCache(id);
if (cached != null) {
return cached; // cache hit
}
Xx db = xxService.getXxById(id); // assume id exists
setXxFromCache(db);
return db;
}
// Delete cache after DB update/delete
public void deleteXxFromCache(long id) {
redisTemplate.delete("Xx:" + id);
}
private void setXxFromCache(Xx xx) {
redisTemplate.opsForValue().set("Xx:" + xx.getId(), xx);
}
private Xx getXxFromCache(int id) {
return redisTemplate.opsForValue().get("Xx:" + id);
}
}
public class XxService {
@Autowired
private XxLazyCache xxLazyCache;
public Xx getXxById(long id) {
// DB query omitted
return new Xx(); // placeholder
}
public void updateXx(Xx xx) {
// update DB omitted
xxLazyCache.deleteXxFromCache(xx.getId());
}
public void deleteXx(long id) {
// delete DB omitted
xxLazyCache.deleteXxFromCache(id);
}
}
public class Xx {
private Long id;
// other fields omitted
}Advantages
Minimizes cache size; only exact‑query keys are cached, preventing cold data from occupying memory.
Low intrusion on CRUD code; cache invalidation is automatic on delete/update.
Plug‑in design allows legacy systems to adopt caching without full pre‑loading.
Disadvantages
Cache size must be bounded; unsuitable for unbounded growth scenarios.
Not ideal for a globally shared cache in microservice architectures.
Summary
Space‑efficient and fits precise‑query workloads.
Works when total data volume is manageable.
Not recommended for microservice scenarios requiring a shared cache.
Redis Combined with Guava Local Cache (Microservice Scenario)
In high‑frequency streaming environments with many devices, a single large Redis cache can become a bottleneck. Pairing Redis with a Guava local cache lets each microservice cache a subset of keys, reducing Redis pressure while providing zero‑latency reads.
Flowchart:
Use case: generate a per‑device increment number for Kafka partitioning.
public class DeviceIncCache {
// Guava local cache
private Cache<String, Integer> localCache = CacheBuilder.newBuilder()
.concurrencyLevel(16)
.initialCapacity(1000)
.maximumSize(10000)
.expireAfterAccess(1, TimeUnit.HOURS)
.build();
@Autowired
private RedisTemplate<String, Integer> redisTemplate;
private static final String DEVICE_INC_COUNT = "device_inc_count"; // Redis atomic counter
private static final String DEVICE_INC_VALUE = "device_inc_value"; // Redis hash: deviceCode → inc
// Retrieve or generate the increment for a device code
public int getInc(String deviceCode) {
// 1. Try local cache
Integer inc = localCache.getIfPresent(deviceCode);
if (inc != null) {
return inc;
}
// 2. Check Redis hash
inc = (Integer) redisTemplate.opsForHash().get(DEVICE_INC_VALUE, deviceCode);
// 3. If absent, generate a new increment using Redis INCR
if (inc == null) {
inc = redisTemplate.opsForValue().increment(DEVICE_INC_COUNT).intValue();
redisTemplate.opsForHash().put(DEVICE_INC_VALUE, deviceCode, inc);
}
// 4. Populate local cache
localCache.put(deviceCode, inc);
return inc;
}
}Advantages
Redis guarantees persistence; the local cache provides ultra‑fast reads, dramatically reducing Redis load in shared‑cache microservice environments.
Guava offers rich APIs, expiration policies, and size limits, preventing cold data from filling memory.
Cache loss on service restart does not affect correctness because Redis remains the source of truth.
Each service instance caches only the devices it handles, optimizing memory usage.
The generated increment can be used for uniform Kafka partitioning or device‑count statistics.
Disadvantages
Implementation complexity increases.
Applicable only to data that is append‑only (no updates after creation).
Summary
Local cache size is controllable with strong expiration policies.
Suitable for microservice and distributed deployments.
Cached values must be immutable after creation.
Delivers superior performance for read‑heavy workloads.
Key Takeaways
Redis provides a rich set of data structures (increment, bitmap, hash, list, etc.) that cover many business needs. Guava local cache complements Redis by offering in‑process, memory‑controlled access with zero connection latency. Combining the two allows developers to balance space efficiency and access speed in real‑world systems.
Signed-in readers can open the original source through BestHub's protected redirect.
This article has been distilled and summarized from source material, then republished for learning and reference. If you believe it infringes your rights, please contactand we will review it promptly.
Java Architect Essentials
Committed to sharing quality articles and tutorials to help Java programmers progress from junior to mid-level to senior architect. We curate high-quality learning resources, interview questions, videos, and projects from across the internet to help you systematically improve your Java architecture skills. Follow and reply '1024' to get Java programming resources. Learn together, grow together.
How this landed with the community
Was this worth your time?
0 Comments
Thoughtful readers leave field notes, pushback, and hard-won operational detail here.
