Boost Performance with Redis Lazy Loading and Guava Local Cache: A Practical Guide

This article explains how to combine Redis lazy‑loading caching with Guava local cache to reduce I/O pressure, improve read performance, and balance memory usage, providing code examples, pros and cons, and suitable scenarios for backend and microservice architectures.

Java High-Performance Architecture
Java High-Performance Architecture
Java High-Performance Architecture
Boost Performance with Redis Lazy Loading and Guava Local Cache: A Practical Guide

Preface

We often use Redis as a cache to improve performance and reduce MySQL pressure. Redis’s flexible data structures are suitable for precise queries and statistics.

However, high‑frequency data streams can put pressure on Redis; the main cost is I/O. To reduce Redis read/write pressure we can use a local cache. Guava provides an easy‑to‑use local cache API with expiration policies.

Design Example

Redis Lazy Loading Cache

Data is written to MySQL without caching; when a precise query occurs the data is cached, achieving “cache on read, no cache on miss”.

Flow diagram:

Code example:

// 伪代码示例 Xx代表你的业务对象 如User Goods等等
public class XxLazyCache {
    @Autowired
    private RedisTemplate<String, Xx> redisTemplate;
    @Autowired
    private XxService xxService;
    /**
     * 查询 通过查询缓存是否存在驱动缓存加载 建议在前置业务保证id对应数据是绝对存在于数据库中的
     */
    public Xx getXx(int id) {
        // 1.查询缓存里面有没有数据
        Xx xxCache = getXxFromCache(id);
        if (xxCache != null) {
            return xxCache; // 卫语句使代码更有利于阅读
        }
        // 2.查询数据库获取数据 我们假定到业务这一步,传过来的id都在数据库中有对应数据
        Xx xx = xxService.getXxById(id);
        // 3.设置缓存、这一步相当于Redis缓存懒加载,下次再查询此id,则会走缓存
        setXxFromCache(xx);
        return xx;
    }
    /**
     * 对xx数据进行修改或者删除操作 操作数据库成功后 删除缓存
     * 删除请求 - 删除数据库数据 删除缓存
     * 修改请求 - 更新数据库数据 删除缓存 下次在查询时候就会从数据库拉取新的数据到缓存中
     */
    public void deleteXxFromCache(long id) {
        String key = "Xx:" + xx.getId();
        redisTemplate.delete(key);
    }
    private void setXxFromCache(Xx xx) {
        String key = "Xx:" + xx.getId();
        redisTemplate.opsForValue().set(key, xx);
    }
    private Xx getXxFromCache(int id) {
        // 通过缓存前缀拼装唯一主键作为缓存Key 如Xxx信息 就是Xxx:id
        String key = "Xx:" + id;
        return redisTemplate.opsForValue().get(key);
    }
}
// 业务类
public class XxServie {
    @Autowired
    private XxLazyCache xxLazyCache;
    public Xx getXxById(long id) {
        // 省略实现
        return xx;
    }
    public void updateXx(Xx xx) {
        // 更新MySQL数据 省略
        // 删除缓存
        xxLazyCache.deleteXxFromCache(xx.getId());
    }
    public void deleteXx(long id) {
        // 删除MySQL数据 省略
        // 删除缓存
        xxLazyCache.deleteXxFromCache(xx.getId());
    }
}
// 实体类
public class Xx {
    // 业务主键
    private Long id;
    // ...省略
}

Advantages:

Guarantees minimal cache size for precise queries, avoiding cold data occupying valuable memory.

Low intrusion on CRUD operations; deletion synchronizes cache.

Pluggable; legacy systems can upgrade without pre‑initializing cache.

Disadvantages:

Data volume must be controllable; not suitable for unbounded growth.

Not ideal for global cache in microservice scenarios.

Summary:

Space minimized.

Fits precise query scenarios.

Total data volume controllable – recommended.

Not suitable for microservice scenarios.

Redis Combined with Local Cache

In microservice environments, many services share a large Redis cache, causing high read pressure. Combining a local cache with Redis reduces this pressure and eliminates connection overhead.

Flow diagram:

Use case: devices upload data at high frequency; each device has a code. An auto‑increment number generated by Redis is used to partition Kafka topics evenly.

Redis hash stores the mapping; a Guava local cache stores recently used increments to avoid frequent Redis reads.

Code example:

/**
 * This cache demonstrates how to combine Redis auto‑increment hash with Guava local cache
 * for device increment generation, caching, and local caching.
 */
public class DeviceIncCache {
    /** Local cache */
    private Cache<String, Integer> localCache = CacheBuilder.newBuilder()
        .concurrencyLevel(16) // 并发级别
        .initialCapacity(1000) // 初始容量
        .maximumSize(10000) // 缓存最大长度
        .expireAfterAccess(1, TimeUnit.HOURS) // 缓存1小时没被使用就过期
        .build();
    @Autowired
    private RedisTemplate<String, Integer> redisTemplate;
    private static final String DEVICE_INC_COUNT = "device_inc_count"; // redis自增数缓存的key
    private static final String DEVICE_INC_VALUE = "device_inc_value"; // redis设备编码对应自增数的hash缓存key
    /** Get device increment */
    public int getInc(String deviceCode) {
        // 1.从本地缓存获取
        Integer inc = localCache.get(deviceCode);
        if (inc != null) {
            return inc;
        }
        // 2.本地缓存未命中,从redis的hash缓存获取
        inc = (Integer) redisTemplate.opsForHash().get(DEVICE_INC_VALUE, deviceCode);
        // 3. redis的hash缓存中没有,说明是新设备,先为设备生成一个自增号
        if (inc == null) {
            inc = redisTemplate.opsForValue().increment(DEVICE_INC_COUNT).intValue();
            // 添加到redis hash缓存
            redisTemplate.opsForHash().put(DEVICE_INC_VALUE, deviceCode, inc);
        }
        // 4.添加到本地缓存
        localCache.put(deviceCode, inc);
        // 5.返回自增数
        return inc;
    }
}

Advantages:

Redis guarantees data persistence; local cache provides ultra‑fast reads, effectively reducing Redis load.

Guava offers rich API, expiration policies, and capacity control, preventing cold data from occupying memory.

Service restarts clear local cache without affecting business.

In distributed scenarios each instance caches only its own devices, optimizing memory usage.

Auto‑increment satisfies both partitioning and device count statistics.

Disadvantages:

Increases code complexity.

Only suitable for cache‑once‑no‑update scenarios.

Summary:

Local cache space is controllable with good expiration.

Fits microservice and distributed scenarios.

Cache content must be immutable.

Performance is excellent.

Postscript

Redis provides many data types and APIs suitable for business development, such as counters (increment/decrement), bitmaps, hashes, and list‑based queues. Guava cache offers efficient reads and flexible control over cache size and eviction.

Understanding these features helps balance space and time in application development.

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.

JavamicroservicesRedisLazy LoadingGuava Cachebackend caching
Java High-Performance Architecture
Written by

Java High-Performance Architecture

Sharing Java development articles and resources, including SSM architecture and the Spring ecosystem (Spring Boot, Spring Cloud, MyBatis, Dubbo, Docker), Zookeeper, Redis, architecture design, microservices, message queues, Git, etc.

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.