Redis Cluster y alta disponibilidad: configuración de caché tolerante a fallos para microservicios
Introducción: por qué necesitas Redis Cluster en una arquitectura de microservicios
La arquitectura de microservicios implica decenas, y a veces cientos, de servicios independientes que pueden acceder a una caché compartida. Una instancia única de Redis en este entorno se convierte en un cuello de botella: no escala horizontalmente, no garantiza alta disponibilidad y está limitada por la memoria de un solo servidor.
Redis Cluster resuelve todos estos problemas a la vez: los datos se distribuyen automáticamente entre varios nodos (sharding), cada nodo maestro tiene réplicas para la conmutación por error, y los clientes saben redirigir las solicitudes cuando cambia la topología. Para microservicios de alto rendimiento que requieren Redis tolerante a fallos con latencia de milisegundos, esta es la solución estándar en producción.
Diferencias entre Redis Sentinel y Redis Cluster: cuándo usar cada uno
Los dos mecanismos principales para garantizar la alta disponibilidad de Redis son Redis Sentinel y Redis Cluster. Cada uno resuelve problemas distintos.
- Redis Sentinel — sistema de monitoreo y failover automático para un único maestro. Sentinel supervisa el maestro, ante su caída elige un nuevo líder entre las réplicas y notifica a los clientes. Los datos no se fragmentan; todo el dataset reside en un único lugar. Ideal para volúmenes de datos moderados y escenarios sencillos.
- Redis Cluster — modo de escalado horizontal con sharding automático de datos entre varios maestros. Cada maestro tiene sus propias réplicas. Cluster soporta hasta 1000 nodos y ofrece crecimiento lineal del rendimiento tanto en lectura como en escritura. Adecuado para grandes volúmenes de datos, alto RPS y necesidades de escalado horizontal.
Cuándo elegir Sentinel: el volumen de datos cabe en una sola máquina, se necesita un failover simple y no hay necesidad de escalar la escritura.
Cuándo elegir Cluster: los datos no caben en un solo servidor, se requiere escalado horizontal, alto RPS en escritura o arquitectura de microservicios con múltiples dominios de datos independientes.
Arquitectura de Redis Cluster: sharding, slots y replicación
Hash Slots
Redis Cluster divide el espacio de claves en 16 384 slots (hash slots). Cada clave se mapea a uno de los slots mediante un hash CRC16. Los slots se distribuyen uniformemente entre los nodos maestros. Por ejemplo, en un clúster de tres maestros:
- Maestro 1: slots 0–5460
- Maestro 2: slots 5461–10922
- Maestro 3: slots 10923–16383
Al añadir o eliminar nodos, los slots migran sin detener el clúster; esto se denomina resharding.
Replicación
Cada nodo maestro puede tener una o más réplicas. La replicación es asíncrona. Ante la caída de un maestro, el clúster realiza automáticamente el failover: una réplica se convierte en el nuevo maestro. Para ello, la mayoría de los nodos maestros deben ponerse de acuerdo (quórum). El mínimo recomendado es 3 maestros + 3 réplicas (6 nodos).
Comandos dentro del clúster
Si la clave se encuentra en otro nodo, Redis devuelve la respuesta MOVED o ASK, y el cliente redirige la solicitud. Los clientes inteligentes almacenan en caché la topología del clúster y acceden directamente al nodo correcto.
Configuración paso a paso de Redis Cluster en Docker y Kubernetes
Configuración en Docker
Creamos un archivo de configuración para cada nodo. Ejemplo de redis-7001.conf:
port 7001\ncluster-enabled yes\ncluster-config-file nodes-7001.conf\ncluster-node-timeout 5000\nappendonly yes\nbind 0.0.0.0\nprotected-mode noCreamos archivos similares para los puertos 7002–7006 y arrancamos los contenedores:
for port in 7001 7002 7003 7004 7005 7006; do\n docker run -d --name redis-$port \\\n --net host \\\n -v $(pwd)/redis-$port.conf:/usr/local/etc/redis/redis.conf \\\n redis:7.2 redis-server /usr/local/etc/redis/redis.conf\ndoneInicializamos el clúster:
redis-cli --cluster create \\\n 127.0.0.1:7001 127.0.0.1:7002 127.0.0.1:7003 \\\n 127.0.0.1:7004 127.0.0.1:7005 127.0.0.1:7006 \\\n --cluster-replicas 1El flag --cluster-replicas 1 indica una réplica por cada maestro. Redis CLI distribuirá los nodos automáticamente.
Configuración de Redis Cluster en Kubernetes
Para Kubernetes usamos un StatefulSet y un Headless Service para que cada Pod tenga un nombre DNS estable.
apiVersion: v1\nkind: Service\nmetadata:\n name: redis-cluster\nspec:\n clusterIP: None\n selector:\n app: redis-cluster\n ports:\n - port: 6379\n targetPort: 6379\n - port: 16379\n targetPort: 16379\n---\napiVersion: apps/v1\nkind: StatefulSet\nmetadata:\n name: redis-cluster\nspec:\n serviceName: redis-cluster\n replicas: 6\n selector:\n matchLabels:\n app: redis-cluster\n template:\n metadata:\n labels:\n app: redis-cluster\n spec:\n containers:\n - name: redis\n image: redis:7.2\n ports:\n - containerPort: 6379\n - containerPort: 16379\n command: [\"redis-server\"]\n args:\n - --cluster-enabled\n - \"yes\"\n - --cluster-config-file\n - /data/nodes.conf\n - --cluster-node-timeout\n - \"5000\"\n - --appendonly\n - \"yes\"\n volumeMounts:\n - name: data\n mountPath: /data\n volumeClaimTemplates:\n - metadata:\n name: data\n spec:\n accessModes: [\"ReadWriteOnce\"]\n resources:\n requests:\n storage: 1GiTras el despliegue, inicializamos el clúster conectándonos a uno de los Pods:
kubectl exec -it redis-cluster-0 -- redis-cli --cluster create \\\n $(kubectl get pods -l app=redis-cluster -o jsonpath='{range.items[*]}{.status.podIP}:6379 {end}') \\\n --cluster-replicas 1Patrones de uso de Redis Cluster desde clientes Go y PHP
Go: librería go-redis
La librería go-redis soporta Redis Cluster de forma nativa mediante redis.NewClusterClient.
package main\n\nimport (\n "context"\n "fmt"\n "github.com/redis/go-redis/v9"\n)\n\nfunc main() {\n rdb := redis.NewClusterClient(&redis.ClusterOptions{\n Addrs: []string{\n "redis-cluster-0.redis-cluster:6379",\n "redis-cluster-1.redis-cluster:6379",\n "redis-cluster-2.redis-cluster:6379",\n },\n // Actualizar slots automáticamente al cambiar la topología\n RouteByLatency: true,\n ReadOnly: true, // leer desde réplicas\n })\n\n ctx := context.Background()\n err := rdb.Set(ctx, "user:1001", "Alice", 0).Err()\n if err != nil {\n panic(err)\n }\n\n val, err := rdb.Get(ctx, "user:1001").Result()\n if err != nil {\n panic(err)\n }\n fmt.Println("user:1001 =>", val)\n}La opción ReadOnly: true permite dirigir las lecturas a las réplicas, reduciendo la carga en los maestros. RouteByLatency selecciona el nodo más cercano según la latencia.
PHP: librerías Predis y phpredis
En PHP los dos clientes más populares son Predis y la extensión phpredis.
Predis:
<?php\nrequire 'vendor/autoload.php';\n\n$client = new Predis\\Client(\n [\n ['host' => 'redis-cluster-0.redis-cluster', 'port' => 6379],\n ['host' => 'redis-cluster-1.redis-cluster', 'port' => 6379],\n ['host' => 'redis-cluster-2.redis-cluster', 'port' => 6379],\n ],\n [\n 'cluster' => 'redis',\n 'parameters' => [\n 'password' => null,\n ],\n ]\n);\n\n$client->set('order:555', json_encode(['status' => 'pending']));\n$value = $client->get('order:555');\necho $value;phpredis (extensión):
<?php\n$redis = new RedisCluster(null, [\n 'redis-cluster-0.redis-cluster:6379',\n 'redis-cluster-1.redis-cluster:6379',\n 'redis-cluster-2.redis-cluster:6379',\n]);\n\n$redis->set('session:abc123', 'user_data');\necho $redis->get('session:abc123');Un detalle importante en PHP: al usar operaciones multi-clave (MGET, MSET), todas las claves deben hashearse en el mismo slot. Para ello se utilizan hash tags — la parte de la clave entre llaves: {user:1001}:profile, {user:1001}:settings. El clúster hashea únicamente la parte entre llaves, garantizando que caigan en el mismo slot.
Gestión del failover y reconexión en el lado del cliente
El failover en Redis Cluster tarda entre 5 y 15 segundos (dependiendo de cluster-node-timeout). Durante ese período, algunas solicitudes terminarán con errores. El cliente debe gestionar correctamente estas situaciones.
Estrategias de manejo de errores
- Retry con backoff exponencial: al recibir el error
CLUSTERDOWNoLOADING, reintentar la solicitud con intervalos crecientes (100ms, 200ms, 400ms...). - Circuit Breaker: si el porcentaje de errores supera un umbral, suspender temporalmente las solicitudes a Redis y usar un fallback (por ejemplo, respuesta vacía o consulta a la base de datos).
- Actualización de topología: al recibir
MOVED, el cliente debe solicitar de inmediato la topología actualizada medianteCLUSTER SLOTSoCLUSTER SHARDS.
Ejemplo de manejo en Go:
func getWithRetry(ctx context.Context, rdb *redis.ClusterClient, key string) (string, error) {\n var val string\n var err error\n for i := 0; i < 3; i++ {\n val, err = rdb.Get(ctx, key).Result()\n if err == nil {\n return val, nil\n }\n if errors.Is(err, redis.Nil) {\n return "", nil // clave no encontrada — no es un error\n }\n time.Sleep(time.Duration(100*(i+1)) * time.Millisecond)\n }\n return "", fmt.Errorf("redis unavailable after retries: %w", err)\n}Monitoreo de Redis Cluster: métricas, alertas y herramientas
Métricas clave
cluster_state— debe serok. Cualquier otro valor requiere una alerta inmediata.cluster_slots_fail— número de slots en estado FAIL. Debe ser 0.connected_slaves— número de réplicas conectadas a cada maestro.used_memoryvsmaxmemory— uso de memoria. Alerta al superar el 80%.instantaneous_ops_per_sec— RPS actual.rejected_connections,evicted_keys— indicadores de sobrecarga.keyspace_hits/keyspace_misses— hit rate de la caché. Debe estar por encima del 90%.
Herramientas de monitoreo
- Redis Exporter + Prometheus + Grafana — el stack estándar. Desplegamos
oliver006/redis_exportercomo sidecar o Deployment independiente, configuramos el scrape en Prometheus y usamos dashboards prediseñados en Grafana (por ejemplo, ID 763). - redis-cli cluster info — diagnóstico manual rápido:
redis-cli -c -h redis-cluster-0.redis-cluster cluster info. - redis-cli --cluster check — verificación de integridad del clúster:
redis-cli --cluster check redis-cluster-0.redis-cluster:6379. - RedisInsight — herramienta GUI oficial de Redis Ltd. con visualización de topología, consultas lentas y memoria.
Ejemplo de alerta en Prometheus
groups:\n - name: redis_cluster\n rules:\n - alert: RedisClusterDown\n expr: redis_cluster_state != 1\n for: 1m\n labels:\n severity: critical\n annotations:\n summary: "Redis Cluster no está en estado OK"\n - alert: RedisMemoryHigh\n expr: redis_memory_used_bytes / redis_memory_max_bytes > 0.85\n for: 5m\n labels:\n severity: warning\n annotations:\n summary: "El uso de memoria de Redis supera el 85%"Antipatrones y errores frecuentes al trabajar con Redis Cluster
- Usar comandos no soportados en Cluster. Los comandos que operan con múltiples claves de distintos slots (MGET, SUNION, EVAL con varias claves) lanzarán un error. Solución: usar hash tags para agrupar claves en un mismo slot, o rediseñar la lógica.
- Almacenar claves "calientes" en un mismo slot. Si una clave recibe el 90% de la carga, su maestro se convertirá en el cuello de botella. Solución: fragmentar las claves calientes manualmente (
user:counter:1,user:counter:2...). - Ignorar las respuestas MOVED/ASK. Los clientes básicos sin soporte para Cluster recibirán errores al acceder a slots ubicados en otro nodo. Usa siempre librerías cliente con soporte nativo de Redis Cluster.
- Ausencia de réplicas. Ejecutar el clúster sin réplicas (
--cluster-replicas 0) significa que la pérdida de cualquier maestro provoca pérdida de datos y degradación del clúster. En producción, siempre al menos una réplica. - cluster-node-timeout demasiado pequeño. Un timeout demasiado agresivo (menos de 2000ms) provoca failovers falsos por latencias de red momentáneas. Valor recomendado: 5000–15000ms.
- Scripts Lua con claves de distintos slots. EVAL en Redis Cluster requiere que todas las claves estén en el mismo slot. Violar esta regla genera el error
CROSSSLOT. - Ausencia de monitoreo de cluster_state. El clúster puede pasar al estado FAIL de forma silenciosa si no hay alertas configuradas. El monitoreo es obligatorio en producción.
Conclusión
Redis Cluster es una solución madura y confiable para implementar una caché tolerante a fallos en arquitecturas de microservicios. El sharding automático en 16 384 slots, el failover integrado y el escalado horizontal lo convierten en la elección natural para sistemas de alto rendimiento donde una instancia única de Redis ya no es suficiente.
Con una configuración correcta —el número adecuado de réplicas, clientes bien implementados en Go o PHP, monitoreo completo con Prometheus y Grafana, y un uso consciente de los hash tags— Redis Cluster proporciona una base sólida para el almacenamiento en caché en entornos Kubernetes y sistemas distribuidos.
Recomendaciones clave para producción: mínimo 3 maestros + 3 réplicas, cluster-node-timeout de al menos 5000ms, monitoreo de cluster_state y used_memory, lógica de reintentos en el cliente y hash tags para operaciones multi-clave. Siguiendo estos principios, dispondrás de un Redis estable, escalable y tolerante a fallos para tus microservicios.
Tecnologías
Etiquetas
Ruslan Ismailov
Desarrollador Senior Web / Backend. Desarrollador senior web/backend con 9 años de experiencia. Stack: PHP, Laravel, PostgreSQL, Redis, Docker, Kubernetes, REST, microservicios, CI/CD. Más sobre mí →