Backend Development 11 min read

Design and Implementation of a High‑Performance Distributed Cache with Redis and Caffeine for Spring Boot Services

This article outlines the design goals, architecture, and implementation details of a high‑performance distributed caching solution for Spring Boot applications, combining Redis as a first‑level cache with Caffeine as a second‑level cache, and provides configuration, usage examples, and future enhancement plans.

Selected Java Interview Questions
Selected Java Interview Questions
Selected Java Interview Questions
Design and Implementation of a High‑Performance Distributed Cache with Redis and Caffeine for Spring Boot Services

Design Purpose

The solution aims to provide a high‑performance distributed cache for Spring Boot services using Redis and Caffeine, reduce integration cost through AOP interception without affecting business logic, and support features such as async operations, timeout control, and compression.

Service Structure

Application services integrate the GEMINI‑CACHE component. The first‑level cache uses Redis (cluster or single instance) wrapped by Redisson, supporting per‑business classification, global and per‑key timeout, serialization, and compression. The second‑level cache uses Caffeine for local high‑speed storage, with synchronization between multiple nodes and Redis.

For high‑real‑time scenarios, two approaches are offered: annotation‑based cache updates on write operations, or a Kafka + Canal listener that updates caches transparently.

Design Implementation

A) Data Caching Process

Intercept interface methods via AOP without altering original logic.

Determine which interfaces require caching based on configuration.

Parse method arguments to generate a cache key (SHA algorithm optional).

Check second‑level Caffeine cache first; if miss, query first‑level Redis, then populate Caffeine.

If Redis miss, execute the original method, store the result in both Redis and Caffeine, and broadcast an update via Redis Pub/Sub for other nodes.

B) High‑Real‑Time Scenarios

Apply @CachePut or @CacheDelete annotations on create/update/delete methods; the annotations trigger broadcast updates to refresh caches across nodes.

C) Consistency Guarantees

Update Redis first, then Caffeine; reads check Caffeine then Redis. If Caffeine fails, Redis provides the data and Caffeine is refreshed. Pub/Sub ensures stale Caffeine entries are cleared.

D) Performance Optimizations

Support thread pools for cache and Redis connections, using Netty for high throughput.

Enable async operations, SHA‑based keys, serialization, compression, request merging, and Lua scripts.

Leverage Caffeine’s Java 8 implementation for optimal hit rates.

Future Plans

Introduce hotspot monitoring via AOP to automatically cache frequently accessed interfaces, and implement Kafka + Canal based real‑time synchronization that listens to data source changes and clears related cache entries.

Usage Instructions

Maven Dependency

<dependency>
    <groupId>com.mirson</groupId>
    <artifactId>gemini-cache</artifactId>
    <version>${gemini.cache.version}</version>
</dependency>

Configuration (application.yml / properties)

# Redis cache settings
app.cache.enable: true
app.cache.enableSecondCache: true
app.cache.redis.executor.maxPoolSize: 16
app.cache.redis.executor.corePoolSize: 8
app.cache.redis.executor.aliveTime: 30
app.cache.redis.executor.queueCapacity: 1000
app.cache.redis.pool.maxSize: 100
app.cache.redis.pool.minIdleSize: 10
app.cache.redis.global.expire: 60
app.cache.redis.pool.maxWaitMills: 3000
app.cache.redis.port: 6379
app.cache.redis.database: 7
app.cache.redis.useCompression: true

# Caffeine cache settings
app.cache.caffeine.expireAfterWrite: 30000
app.cache.caffeine.initialCapacity: 0
app.cache.caffeine.maximumSize: 0

Annotation Example for Caching a Query

/**
 * Get order information by order number
 * @param orderNo
 * @return
 */
@Cacheable(cacheName = "gemini_cache_order", keyExpression = "#param1", TTL = 10)
@Override
public String getOrder(String orderNo) {
    log.info("enter getOrder method, orderNo: " + orderNo);
    return "get order, orderNo: " + orderNo;
}

Key attributes:

cacheName : logical cache identifier.

keyExpression : Spring EL expression for the cache key.

TTL : time‑to‑live in seconds (0 means unlimited).

isAsync : whether to store cache asynchronously.

keyGenerator : default SHA‑based generator for unique keys.

Real‑Time Cache Synchronization

Apply @CachePut on update methods to broadcast changes:

/**
 * Update order information
 * @param orderNo
 * @return
 */
@CachePut(cacheNames = "gemini_cache_order", keyExpression = "#param1", TTL = 10)
@Override
public String updateOrder(String orderNo) {
    log.info("enter updateOrder method, orderNo: " + orderNo);
    return "get order, update orderNo: " + orderNo;
}

Testing Workflow

1. First request triggers method execution, stores result in Redis and Caffeine, and logs the call. 2. Subsequent request retrieves data directly from Caffeine (no log output). 3. Updating the order via @CachePut logs the update and forces cache refresh; subsequent queries return the updated data.

Source Code

GitHub repository: https://github.com/mirson6/gemini-cache

JavaAOPRedisSpring Bootcaffeinedistributed cache
Selected Java Interview Questions
Written by

Selected Java Interview Questions

A professional Java tech channel sharing common knowledge to help developers fill gaps. Follow us!

0 followers
Reader feedback

How this landed with the community

login 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.