Implementing Caching with Redis in Spring Cloud: Local Cache, Distributed Cache, and Locking Strategies

This article explains how to improve Spring Cloud microservice performance by adding local and Redis distributed caching, demonstrates the necessary code changes, and discusses cache pitfalls such as penetration, avalanche, and breakdown along with lock‑based solutions.

Wukong Talks Architecture
Wukong Talks Architecture
Wukong Talks Architecture
Implementing Caching with Redis in Spring Cloud: Local Cache, Distributed Cache, and Locking Strategies

In high‑concurrency internet systems, caching is essential for achieving the "three highs" of performance, availability, and scalability. The article first introduces why caching is needed, what data is suitable for caching, and the basic read‑through cache flow.

1. Cache

1.1 Why Use Cache

Caching stores frequently accessed data in memory to reduce database I/O, lower latency, and avoid deadlocks, but only data that is timely, has low consistency requirements, or has high read‑frequency with low update frequency should be cached.

1.2 Local Cache

Using an in‑memory HashMap (or similar) is the simplest form of cache. The article shows a REST API that queries a list of question types directly from the database without caching, then adds a HashMap to store the result.

@RequestMapping("/list")
public R list(){
    // from DB
    typeEntityList = ITypeService.list();
    return R.ok().put("typeEntityList", typeEntityList);
}

After adding a local cache:

private Map<String, Object> cache = new HashMap<>();
List<TypeEntity> typeEntityListCache = (List<TypeEntity>) cache.get("typeEntityList");
if(typeEntityListCache == null){
    System.out.println("The cache is empty");
    List<TypeEntity> typeEntityList = ITypeService.list();
    cache.put("typeEntityList", typeEntityList);
    typeEntityListCache = typeEntityList;
}
return R.ok().put("typeEntityList", typeEntityListCache);

Local cache reduces DB access but has drawbacks: memory consumption, loss on restart, possible data inconsistency, and issues in clustered environments.

2. Redis Cache

2.1 Install Redis via Docker

The article recommends installing Redis with Docker on Ubuntu or macOS (M1) and provides reference links.

2.2 Add Redis Dependency

<dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-data-redis</artifactId>
</dependency>

2.3 Test Redis

Using StringRedisTemplate to store and retrieve a simple key/value pair:

@Autowired
StringRedisTemplate stringRedisTemplate;

@Test
public void TestStringRedisTemplate(){
    ValueOperations<String, String> ops = stringRedisTemplate.opsForValue();
    ops.set("悟空", "悟空聊架构_" + UUID.randomUUID().toString());
    String wukong = ops.get("悟空");
    System.out.println(wukong);
}

2.4 Refactor Business Logic with Redis

Replace the HashMap with Redis, serialize objects to JSON before storing, and deserialize when reading:

public List<TypeEntity> getTypeEntityList(){
    ValueOperations<String, String> ops = stringRedisTemplate.opsForValue();
    String cache = ops.get("typeEntityList");
    if(StringUtils.isEmpty(cache)){
        System.out.println("The cache is empty");
        List<TypeEntity> fromDb = this.list();
        cache = JSON.toJSONString(fromDb);
        ops.set("typeEntityList", cache);
        return fromDb;
    }
    return JSON.parseObject(cache, new TypeReference<List<TypeEntity>>(){});
}

Testing with Postman shows the first request is slower (cache miss) and subsequent requests are fast (cache hit).

3. Cache Pitfalls

3.1 Cache Penetration

When a non‑existent key is repeatedly queried, the database is hit each time. The solution is to cache the null result with a short TTL.

3.2 Cache Avalanche

If many keys share the same expiration time, they may all expire simultaneously, causing a surge of DB traffic. Adding a random offset to TTL mitigates this.

3.3 Cache Breakdown

When a hot key expires, a flood of requests may hit the DB. Using a lock so that only one request repopulates the cache prevents overload.

4. Lock‑Based Solution for Cache Breakdown

Using a synchronized block (local lock) to ensure only one thread accesses the DB when the cache is empty:

public List<TypeEntity> getTypeEntityListByLock(){
    synchronized(this){
        String cache = stringRedisTemplate.opsForValue().get("typeEntityList");
        if(!StringUtils.isEmpty(cache)){
            return JSON.parseObject(cache, new TypeReference<List<TypeEntity>>(){});
        }
        System.out.println("The cache is empty");
        List<TypeEntity> fromDb = this.list();
        cache = JSON.toJSONString(fromDb);
        stringRedisTemplate.opsForValue().set("typeEntityList", cache, 1, TimeUnit.DAYS);
        return fromDb;
    }
}

The article notes that local locks only work within a single service instance; in a distributed environment they can cause inconsistencies, and a distributed lock (to be covered in the next article) is needed.

5. Problems with Local Locks

When multiple microservice instances use local synchronized locks for stock deduction, race conditions can cause overselling because each instance sees the same cached stock value.

The article concludes by previewing the next part, which will cover distributed lock implementation.

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.

javaperformanceMicroservicesrediscachingSpring Boot
Wukong Talks Architecture
Written by

Wukong Talks Architecture

Explaining distributed systems and architecture through stories. Author of the "JVM Performance Tuning in Practice" column, open-source author of "Spring Cloud in Practice PassJava", and independently developed a PMP practice quiz mini-program.

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.