Redis est une base de données en mémoire haute performance.
Réplication Maître-Esclave
La réplication maître-esclave permet de copier les données d'un serveur Redis vers d'autres serveurs. Le serveur d'origine est le maître (Master) et les serveurs récepteurs sont les esclaves (Slave). La copie des données est unidirectionnelle, du maître vers les esclaves. Cette configuration optimise les opérations d'écriture sur le maître et les opérations de lecture sur les esclaves.
Avantages :
- Séparation des responsabilités lecture/écriture, améliorant les performances globales.
- Possibilité de déployer plusieurs esclaves pour gérer un trafic de lecture important, offrant ainsi une résilience accrue en cas de défaillance d'un esclave.
Configuration de la réplication :
- Modifier le fichier de configuration
redis.windows.confdu maître pour définir le port (par exemple, 6001) et celui de l'esclave (par exemple, 6002).
# Accepte les connexions sur le port spécifié, par défaut 6379.
port 6001
- Démarrer Redis avec la configuration modifiée :
redis-server.exe redis.windows.conf. - Utiliser la commande
INFO REPLICATIONpour vérifier l'état de la réplication. - Connecter un client à l'instance de l'esclave (port 6002) et exécuter la commande
REPLICAOF 127.0.0.1 6001pour désigner le serveur 6001 comme maître.
Après configuration, le rôle de l'instance 6002 sera slave. Les deux instances maintiennent un décalage de réplication (replication offset) qui indique la synchronisation des données. Une différence notable suggère la nécessité d'une synchronisation incrémentielle.
Le maître supporte les opérations de lecture et d'écriture. Les esclaves supportent uniquement la lecture. L'ajout d'un nouvel esclave entraîne la synchronisation immédiate des données du maître. Les esclaves restent accessibles en lecture même si le maître est indisponible.
Processus de synchronisation :
- Après l'exécution de
REPLICAOF [ip] [port], l'esclave enregistre l'adresse du maître. - L'esclave tente d'établir une connexion réseau avec le maître via une tâche planifiée.
- Une fois connecté, une copie complète des données du maître est effectuée (full resynchronization), suivie d'une synchronisation incrémentielle (incremental synchronization) des nouvelles commandes d'écriture.
Suppression du mode réplication :
Pour désactiver le mode réplication, connectez-vous à l'esclave et exécutez la commande REPLICAOF NO ONE.
Alternativement, la configuration peut être définie directement dans le fichier redis.windows.conf de l'esclave en ajoutant la ligne REPLICAOF 127.0.0.1 6001.
Un esclave peut également avoir son propre esclave. Ce mode de réplication en chaîne peut cependant introduire des retards de synchronisation si un maillon de la chaîne pose problème.
Mode Sentinelle
Le mode Sentinelle ajoute une couche de supervision pour gérer la défaillance du maître. Les sentinelles surveillent l'état de toutes les instances. En cas de détection d'une indisponibilité du maître, elles initient un vote parmi les esclaves pour élire un nouveau maître, assurant ainsi la continuité du service. Une configuration minimale requiert au moins un maître et un esclave.
Configuration d'une Sentinelle :
- Modifier le fichier de configuration de la Sentinelle (par exemple,
sentinel.conf). Supprimer le contenu existant et ajouter :
sentinel monitor mymaster 127.0.0.1 6001 1
mymasterest un nom arbitraire pour le groupe de maîtres surveillés. Les chiffres suivants indiquent l'adresse IP et le port du maître. Le dernier chiffre (1) représente le seuil de sentinelles qui doivent s'accorder sur la défaillance du maître pour déclencher un basculement.- Démarrer la Sentinelle :
redis-server.exe redis.windows.conf --sentinel.
La sentinelle surveille le maître et identifie les esclaves. Si le maître tombe en panne, la sentinelle élit un nouvel esclave pour devenir maître. L'ancien maître, s'il redémarre, deviendra un esclave du nouveau maître.
Règles d'élection du nouveau maître par la Sentinelle :
- Priorité : Configurée via
replica-prioritydans le fichier de configuration (valeur plus basse = priorité plus élevée). - Décalage de réplication (Replication Offset) : L'esclave avec le décalage le plus élevé est préféré.
RUNID: En cas d'égalité, l'esclave avec leRUNIDle plus petit (généré aléatoirement au démarrage) est choisi.
Gestion des défaillances de Sentinelles :
Pour assurer la résilience, plusieurs sentinelles doivent être déployées. Il suffit de copier le fichier de configuration de la sentinelle, de changer le port et d'ajuster le seuil de consensus (par exemple, passer à 2 pour nécessiter l'accord de deux sentinelles).
sentinel monitor mymaster 127.0.0.1 6001 2
port 2002
Client Java avec Jedis Sentinel :
Pour qu'un client Java connaisse le nouveau maître après un basculement, utilisez JedisSentinelPool.
<dependency>
<groupId>redis.clients</groupId>
<artifactId>jedis</artifactId>
<version>4.2.1</version>
</dependency>
import redis.clients.jedis.Jedis;
import redis.clients.jedis.JedisSentinelPool;
import java.util.Arrays;
import java.util.HashSet;
public class Main {
public static void main(String[] args) {
// Utiliser JedisSentinelPool pour obtenir une connexion au Maître actuel
// Si les sentinelles ne sont pas joignables, désactiver le mode protégé dans leur configuration.
JedisSentinelPool pool = new JedisSentinelPool("mymaster", new HashSet<>(Arrays.asList(
"127.0.0.1:2001", "127.0.0.1:2002", "127.0.0.1:2003"
)));
// Obtenir une instance Jedis connectée au maître
Jedis jedis = pool.getResource();
jedis.set("testKey", "testValue");
// Obtenir une autre instance Jedis
Jedis jedis2 = pool.getResource();
System.out.println(jedis2.get("testKey")); // Vérifier la lecture
pool.close(); // Fermer le pool
}
}
Architecture en Cluster
Le mode Cluster permet d'étendre la capacité de stockage de Redis en répartissant les données sur plusieurs nœuds. Le cluster utilise un système de 16384 "slots" (emplacements) pour distribuer les données. Chaque nœud du cluster est responsable d'un sous-ensemble de ces slots.
La détermination du slot pour une clé donnée est calculée à l'aide d'une fonction de hachage (CRC16) suivie d'une opération modulo : slot = CRC16(key) % 16384.
Essentiellement, le cluster répartit les clés entre les différents nœuds via un algorithme de hachage.
Configuration d'un cluster Redis :
- Créer des fichiers de configuration pour 6 instances Redis (3 maîtres et 3 esclaves). Activer le mode cluster dans chaque fichier de configuration :
cluster-enabled yes
- Lancer le processus de création du cluster avec la commande
redis-cli:
redis-cli --cluster create --cluster-replicas 1 127.0.0.1:6001 127.0.0.1:6002 127.0.0.1:6003 127.0.0.1:7001 127.0.0.1:7002 127.0.0.1:7003
--cluster-replicas 1 spécifie qu'un esclave doit être créé pour chaque nœud maître.
- Confirmer la création en tapant
yes.
Une fois le cluster opérationnel, il est possible de s'y connecter en mode cluster en utilisant l'option -c avec redis-cli. Les requêtes seront automatiquement redirigées vers le nœud approprié :
redis-cli -p 6001 -c
SET clusterKey clusterValue
Pour visualiser la topologie du cluster, utilisez la commande CLUSTER NODES.
En cas de défaillance d'un nœud maître, son esclave lui succède en tant que nouveau maître. Si le nœud défaillant redémarre, il devient un esclave du nouveau maître. Si un nœud maître et son esclave deviennent inaccessibles, les données associées à ces slots ne seront plus disponibles jusqu'à leur rétablissement.
Client Java avec JedisCluster :
Pour interagir avec un cluster Redis depuis Java, utilisez JedisCluster.
import redis.clients.jedis.HostAndPort;
import redis.clients.jedis.JedisCluster;
import java.util.HashSet;
public class ClusterExample {
public static void main(String[] args) {
// Se connecter à n'importe quel nœud du cluster. Jedis s'occupera de découvrir les autres.
HostAndPort hap = new HostAndPort("127.0.0.1", 6001);
JedisCluster cluster = new JedisCluster(hap, 5000); // 5000ms timeout
System.out.println("Nombre d'instances dans le cluster : " + cluster.getClusterNodes().size());
cluster.set("myClusterKey", "myClusterValue");
System.out.println(cluster.get("myClusterKey"));
cluster.close(); // Fermer la connexion au cluster
}
}
Verrou Distribué avec Redis
Redis peut implémenter des verrous distribués à l'aide de la commande SETNX key value (Set if Not Exists). Cette commande ne réussit que si la clé spécifiée n'existe pas, ce qui permet à un seul client d'acquérir le verrou.
Pour éviter les blocages permanents en cas de crash d'un client, il est crucial d'ajouter une expiration au verrou. La commande SET key value EX seconds NX permet de combiner l'acquisition conditionnelle (NX) avec une durée de vie (EX).
SET myLock myUniqueValue EX 60 NX
Cependant, une simple expiration peut entraîner des problèmes si le délai d'expiration est trop court. Une autre approche consiste à associer une valeur unique (par exemple, un UUID) à la clé du verrou. Lors de la suppression du verrou, le client vérifie si la valeur correspond à la sienne avant de procéder à la suppression, évitant ainsi de supprimer un verrou acquis par un autre client.
Un scénario encore plus complexe survient lorsque la vérification de la valeur et la supppression du verrou ne sont pas atomiques par rapport à l'expiration du verrou. Si le verrou expire juste avant la suppression, un autre client peut acquérir le verrou, et la tentative de suppression du premier client pourrait alors supprimer le verrou du second.
Le framework Redisson, recommandé par Redis, offre une solution robuste pour les verrous distribués. Il intègre un mécanisme de "chien de garde" (watchdog) qui renouvelle automatiquement la durée de vie du verrou tant que l'instance Redisson est active.
Utilisation de Redisson pour les verrous :
- Ajouter les dépendances Maven :
<dependency>
<groupId>org.redisson</groupId>
<artifactId>redisson</artifactId>
<version>3.5.0</version>
</dependency>
<dependency>
<groupId>io.netty</groupId>
<artifactId>netty-all</artifactId>
</dependency>
- Écrire le code :
import org.redisson.Redisson;
import org.redisson.api.RLock;
import org.redisson.api.RedissonClient;
import org.redisson.config.Config;
import redis.clients.jedis.Jedis;
public class RedissonLockExample {
public static void main(String[] args) {
Config config = new Config();
// Configuration pour une instance Redis unique
config.useSingleServer().setAddress("redis://127.0.0.1:6379");
RedissonClient redisson = Redisson.create(config);
Jedis jedisCounter = new Jedis("127.0.0.1", 6379); // Pour l'exemple du compteur
for (int i = 0; i < 10; i++) {
new Thread(() -> {
RLock lock = redisson.getLock("myDistributedLock");
try {
lock.lock(); // Acquisition du verrou
// Opération critique
int currentValue = Integer.parseInt(jedisCounter.get("counter"));
jedisCounter.set("counter", String.valueOf(currentValue + 1));
} finally {
lock.unlock(); // Libération du verrou
}
}).start();
}
// Attendre un peu pour que les threads se terminent et fermer les ressources
try { Thread.sleep(2000); } catch (InterruptedException e) { e.printStackTrace(); }
redisson.shutdown();
jedisCounter.close();
}
}
Pour une haute disponibilité, Redisson supporte le RedLock, qui distribue le verrou sur plusieurs instances Redis. Le verrou est considéré comme acquis si la majorité des instances l'obtiennent, assurant ainsi la continuité même en cas de défaillance de certaines instances.