Dans cet article, nous allons découvrir Redis et apprendre à l’utiliser dans une application Spring Boot pour mettre en cache des données. Nous aborderons les concepts de base de Redis, son intégration dans un projet Java / Spring, une mise en œuvre du cache via l’annotation @Cacheable, puis nous évoquerons les avantages et inconvénients de cette technologie.
Technologies utilisées : Spring Boot (Spring Data Redis), Java > 17, Docker, Redis (serveur 7.x)
Redis est une base de données NoSQL de type clé-valeur fonctionnant entièrement en mémoire. Il s’agit d’un projet open source réputé pour sa grande rapidité d’exécution. Concrètement, Redis stocke les données en RAM plutôt que sur un disque par défaut, ce qui permet d'avoir des performances d’accès en lecture/écriture élevées. Ses principaux cas d’utilisation incluent la mise en cache de données, la gestion de sessions, le système de messagerie pub / sub (publication / souscription).
Une utilisation courante de Redis est d’agir comme cache afin d’accélérer les applications. Lorsqu’une application interroge une base de données classique pour récupérer des informations fréquemment demandées, cela peut engendrer de la latence et une charge importante sur la base de données. En insérant Redis entre l’application et la base de données, on stocke en mémoire les données fréquemment consultées pour les servir plus rapidement.
Pour utiliser Redis dans notre application, nous avons besoin d’installer ou de lancer un serveur Redis, puis de configurer notre projet Spring Boot afin qu’il puisse communiquer avec ce serveur.
Lancer un serveur Redis localement : La méthode la plus simple pour démarrer Redis en local consiste à utiliser Docker. Si Docker est installé sur votre machine, exécutez la commande suivante dans un terminal :
# Démarrer un serveur Redis (port par défaut 6379)
docker run -p 6379:6379 -it --rm redis/redis-stack-server
Cette commande télécharge et lance l’image officielle de Redis en exposant le port par défaut 6379. Une fois le conteneur démarré, un serveur Redis tourne en arrière-plan en local.
Remarque : Si vous ne souhaitez pas utiliser Docker, vous pouvez installer Redis directement sur votre système via les binaires disponibles sur le site officiel.
Dépendances Maven/Gradle : Côté application Spring Boot, il est nécessaire d'ajouter la dépendance Spring Data Redis. Modifier comme suit le fichier pom.xml :
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-data-redis</artifactId>
</dependency>
Cette dépendance apporte le nécessaire pour communiquer avec Redis (client Lettuce par défaut, template Spring, etc.).
Activation du cache dans Spring Boot : Par défaut, l’annotation de cache (@Cacheable) n’est pas active tant qu’elle n'est pas explicitement demandée. Pour cela, ajouter l’annotation @EnableCaching. Par exemple :
@Configuration
@EnableCaching
public class RedisConfig {
}
En ajoutant @EnableCaching, Spring Boot va automatiquement configurer un gestionnaire de cache (CacheManager) associé à Redis, grâce à la présence de la dépendance Spring Data Redis sur le classpath. Autrement dit, toutes les annotations de cache que nous utiliserons seront prises en charge et stockeront les données dans Redis (plutôt que dans une cache en mémoire locale ou autre).
Ci-dessous un exemple de configuration du cache avec Redis en modifiant la configuration par défaut.
@Configuration
@EnableCaching
public class RedisConfig {
@Bean
RedisConnectionFactory jedisConnectionFactory() {
final RedisStandaloneConfiguration redisStandaloneConfiguration = new RedisStandaloneConfiguration();
redisStandaloneConfiguration.setHostName("localhost");
redisStandaloneConfiguration.setPort(6379);
return new LettuceConnectionFactory(redisStandaloneConfiguration);
}
@Bean
public RedisCacheManager cacheManager(final RedisConnectionFactory redisConnectionFactory) {
final RedisCacheConfiguration cacheConfiguration = RedisCacheConfiguration.defaultCacheConfig()
.entryTtl(Duration.ofSeconds(30))
.disableCachingNullValues()
.serializeValuesWith(RedisSerializationContext.SerializationPair.fromSerializer(new GenericJackson2JsonRedisSerializer()));
return RedisCacheManager.builder(redisConnectionFactory)
.cacheDefaults(cacheConfiguration)
.build();
}
}
On crée ici tout d'abord une connexion avec Redis via RedisConnectionFactory, en utilisant le client Lettuce, qui est non-bloquant et adapté aux environnements modernes. La connexion se fait vers un Redis local, sur le port 6379.
Ensuite, on définit la gestion du cache à l’aide d’un RedisCacheManager. Celui-ci repose sur une configuration par défaut qui applique une durée de vie (TTL) de 30 secondes à chaque entrée (entryTtl). On désactive également la mise en cache des valeurs nulles (disableCachingNullValues), ce qui permet d’éviter des accès inutiles à Redis pour des résultats vides. Enfin, on précise comment les valeurs sont (dé)sérialisées : ici via GenericJackson2JsonRedisSerializer, qui transforme les objets Java en JSON, facilitant leur lecture et leur compatibilité.
Grâce à cette configuration, il devient très simple d’utiliser le cache en annotant directement vos méthodes avec @Cacheable, sans gérer manuellement la logique de lecture/écriture dans Redis.
L’un des usages les plus puissants de Redis est de servir de cache distribué pour votre application. Spring Boot intègre un mécanisme de cache unifié qui permet de cacher les résultats de certaines fonctions, sans écrire explicitement du code de stockage/recherche dans Redis à chaque appel. Ce mécanisme s’appuie sur les annotations telles que @Cacheable, @CacheEvict, @CachePut, etc.
Nous allons illustrer l’utilisation de @Cacheable avec Redis à travers un exemple. Imaginons une application qui gère des utilisateurs via une classe UserService. Sans cache, chaque appel à UserService.getUserById(id) irait interroger une base de données (ou une source de données lente) pour récupérer l’utilisateur correspondant. Avec le cache, nous voulons que la première requête charge l’utilisateur depuis la base puis le stocke dans Redis, et que les appels suivants récupèrent directement l’utilisateur depuis Redis sans interroger la base.
Voici comment on peut procéder :
@Service
public class UserService {
@Autowired
private UserRepository userRepository;
/** Récupère un utilisateur par son ID, en le mettant en cache Redis. */
@Cacheable(value = "user", key = "'user:' + #id", unless = "#result == null")
public UserModel getUserById(Long id) {
return userRepository.findById(id)
.orElseThrow(() -> new RuntimeException("User not found"));
}
/** Récupère la liste des utilisateurs, avec mise en cache. */
@Cacheable(value = "userItems")
public List<UserModel> listAllUsers() {
return userRepository.findAll();
}
}
Dans ce code :
Vérification du cache dans Redis :
Une fois ces caches en place, on peut exécuter l’application et appeler quelques fois les méthodes getUserById et listAllUsers. La première invocation sera plus lente (accès aux données réelles) et les suivantes plus rapides (données retournées depuis la RAM de Redis). Pour voir les entrées créées dans Redis, on peut utiliser l’interface en ligne de commande redis-cli. Par exemple, après avoir appelé listAllUsers() et getUserById(2), ouvrez un terminal et lancez :
$ docker run -it --network host --rm redis redis-cli -h localhost -p 6379
127.0.0.1:6379> KEYS *
1) "userItems::SimpleKey []"
2) "user::user:2"
On constate que deux clés sont présentes : userItems::SimpleKey [] et user::user:2. Ces clés correspondent respectivement au résultat mis en cache de listAllUsers() et de getUserById(2). On peut inspecter leur contenu avec la commande GET :
127.0.0.1:6379> TYPE "userItems::SimpleKey []"
string
127.0.0.1:6379> GET "userItems::SimpleKey []"
"[\"java.util.ArrayList\",[{\"@class\":\"com.laulem.redis.example.model.UserModel\",\"id\":2,\"firstName\":\"Alexandre\",\"lastName\":\"Lemaire\"}]]"
127.0.0.1:6379> GET "user::user:2"
"{\"@class\":\"com.laulem.redis.example.model.UserModel\",\"id\":2,\"firstName\":\"Alexandre\",\"lastName\":\"Lemaire\"}"
On observe que chaque entrée est stockée en tant que string (chaîne de caractères). Le contenu est du JSON incluant le nom de la classe Java (UserModel) et les champs de l’objet. Spring Data Redis a ici utilisé un sérialiseur JSON pour sauvegarder nos objets de manière lisible.
En utilisant @Cacheable, nous avons ajouté une couche de cache transparente pour l’appelant : il n’a pas besoin de savoir si la donnée vient directement de la base ou de Redis. Spring se charge de la logistique, et Redis agit comme un tampon ultra-rapide. Lorsque les données changent dans la base, il peut être nécessaire d’invalider le cache pour éviter de servir des informations obsolètes : c’est le rôle de l’annotation @CacheEvict ou de mécanismes de TTL (expiration automatique des entrées).
Prenons l’exemple suivant dans la classe UserService :
public class UserService {
....
@Caching(evict = {
@CacheEvict(value = "userItems", allEntries = true),
@CacheEvict(value = "user", key = "'user:' + #id")
})
public void delete(final Long id) {
userRepository.deleteById(id);
}
}
Dans ce code, la méthode delete(Long id) supprime un utilisateur en base. Elle est annotée avec @Caching, qui permet de combiner plusieurs évictions de cache en une seule opération. On y trouve deux @CacheEvict :
Ce mécanisme d’éviction manuelle permet donc de synchroniser le cache avec les opérations de modification ou suppression des données. Il est également possible d’utiliser @CachePut pour forcer la mise à jour du cache après une modification.
Grâce à @CacheEvict, le cache reste cohérent avec la source de vérité (la base de données), tout en continuant à profiter de Redis pour accélérer les accès en lecture.
Nous avons vu que Redis pouvait accélérer une application grâce au cache. Cependant, il présente des points forts et des limites.
Avantages de Redis :
Inconvénients de Redis :
Dans cet article, nous avons exploré Redis et son utilisation en tant que cache dans une application Spring Boot. Nous avons vu comment installer et configurer Redis, puis utiliser les annotations Spring pour mettre en place un mécanisme de cache efficace sans complexité excessive. Nous avons également exploré quelques avantages et inconvénients quant à son utilisation.
Redis est un composant incontournable pour optimiser les performances d’une application. Qu’il soit utilisé en cache simple ou dans des scénarios plus élaborés (systèmes de messagerie en temps réel, traitement de flux, etc.).
À noter que cet article, pour des besoins de test, a été en partie rédigé par une IA et presque entièrement reformulé, complété et vérifié par un humain.