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.

Java Architect Essentials
Java Architect Essentials
Java Architect Essentials
How to Combine Redis and Guava for Lazy‑Loading Cache in Java

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.

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.

javaMicroservicesrediscachingGuavalocal cachelazy loading
Java Architect Essentials
Written by

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.

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.