Gestion de la cohérence du cache avec Spring Boot et Redis

Problématiques de cohérence des données

Lors de l'utilisation combinée de Spring Boot et Redis, maintenir la synchronisation entre les données du cache et celles de la base de données représente un défi majeur. Les incohérences surviennent fréquemment dans des environnements hautement concurrentiels lorsque les mises à jour du cache et de la base ne sont pas atomiques.

Origines des incohérences

  • Stratégie de mise à jour inadéquate : Séquence incorrecte des opérations entre cache et base de données
  • Accès concurrentiels : Modifications parallèles non synchronisées
  • Latence réseau : Délais différentiels lors des écritures

Stratégies de résolution

Politique de mise à jour du cache

Privilégier l'approche "base de données d'abord" : modifier la base avant le cache pour garantir la source de vérité.

@Service
public class ClientService {

    @Autowired
    private ClientRepository repository;

    @Cacheable(value = "clients", key = "#identifiant")
    public Client obtenirClient(Long identifiant) {
        return repository.trouverParId(identifiant).orElse(null);
    }

    @CachePut(value = "clients", key = "#client.id")
    public Client modifierClient(Client client) {
        return repository.sauvegarder(client);
    }
}

Invalidation du cache

Suppprimer les entrées du cache lors des modifications pour forcer un rechargement ultérieur.

@CacheEvict(value = "clients", key = "#identifiant")
public void supprimerClient(Long identifiant) {
    repository.effacerParId(identifiant);
}

Cohérence par écriture atomique

Utiliser les transactions pour lier les mises à jour de la base et du cache.

@Transactional
public Client mettreAJourClient(Client client) {
    Client clientModifie = repository.sauvegarder(client);
    redisTemplate.opsForValue().set("client::" + client.getId(), clientModifie);
    return clientModifie;
}

Mise à jour asynchrone

Découpler les mises à jour du cache pour améliorer la réactivité.

@Async
public void rafraichirCache(Client client) {
    redisTemplate.opsForValue().set("client::" + client.getId(), client);
}

Séparation lecture/écriture

Distinguer les flux de lecture (cache) et d'écriture (base de données).

@Cacheable(value = "clients", key = "#identifiant")
public Client lireClient(Long identifiant) {
    return repository.trouverParId(identifiant).orElse(null);
}

@Transactional
public Client ecrireClient(Client client) {
    Client resultat = repository.sauvegarder(client);
    redisTemplate.opsForValue().set("client::" + client.getId(), resultat);
    return resultat;
}

Étiquettes: Spring Boot Redis Cache Jedis Spring Data Redis

Publié le 16 juillet à 23h47