Redis Distributed Cache

When You Need Redis

With 4 ShopEase instances behind a load balancer, each Caffeine cache is separate: an update on server 1 evicts only server 1's copy. Redis is a shared, external cache, so all instances see the same entries and evictions.

+-----------+
 user ---> |   Load    | ---> app-1 --+
           |  Balancer | ---> app-2 --+---> Redis (shared cache) ---> MySQL (on miss)
           +-----------+ ---> app-3 --+

Setup

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

Example

application.yml

spring:
  cache:
    type: redis
    redis:
      time-to-live: 10m
      key-prefix: "shopease::"
      cache-null-values: false
  data:
    redis:
      host: ${REDIS_HOST:localhost}
      port: 6379
      password: ${REDIS_PASSWORD:}
@Bean
RedisCacheManagerBuilderCustomizer redisCaches() {
    var json = RedisSerializationContext.SerializationPair
        .fromSerializer(new GenericJackson2JsonRedisSerializer());   // store JSON, not Java serialization
    return builder -> builder
        .withCacheConfiguration("products",
            RedisCacheConfiguration.defaultCacheConfig().entryTtl(Duration.ofMinutes(10)).serializeValuesWith(json))
        .withCacheConfiguration("stock",
            RedisCacheConfiguration.defaultCacheConfig().entryTtl(Duration.ofSeconds(15)).serializeValuesWith(json));
}

Looking Inside Redis

Example

The same @Cacheable code now stores entries in Redis:

docker run -d --name redis -p 6379:6379 redis:7
curl -s localhost:8080/api/products/3 > /dev/null
redis-cli KEYS "shopease::*"
redis-cli TTL "shopease::products::3"

Example output

1) "shopease::products::3"
(integer) 597
CaffeineRedis
Locationinside the JVMseparate server
Speednanosecondsabout 1 ms (network)
Shared across instancesnoyes
Survives restartnoyes (if persistence is on)