Más allá de la caché de Drupal
En resumen: Configuración paso a paso de Redis como backend de caché en Drupal 10/11 para sitios institucionales con miles de nodos. Instalación, settings.redis.php, bootstrap, failsafe y política de evicción.
- Instala Redis Server y la extensión PhpRedis (más rápida que Predis)
- Configura settings.redis.php con cache.default, locks y flood control
- Define maxmemory y política de evicción volatile-lfu
- Activa el failsafe integrado y monitoriza con redis-cli MONITOR
Drupal, por defecto, almacena toda su caché en la base de datos. Para un sitio con poco tráfico esto funciona bien: las consultas son rápidas, la tabla cache_* tiene pocas filas y el overhead es negligible. El problema aparece cuando el sitio crece.
En uno de los portales institucionales que lideré, con miles de nodos y decenas de tipos de contenido, la base de datos se convirtió en el cuello de botella. Cada petición generaba decenas de consultas a las tablas de caché, y en momentos de tráfico concurrente la latencia se disparaba. No era un problema de código ni de consultas lentas: era un problema de arquitectura. La base de datos estaba haciendo trabajo que debería hacer un almacén en memoria.
Redis resuelve esto de raíz. Es un almacén clave-valor en memoria que opera en el rango de microsegundos, y el módulo de Drupal lo integra como backend de caché, locks, control de inundación y colas. La diferencia es inmediata y medible.
Qué es Redis y por qué importa
Redis (Remote Dictionary Server) es un almacén de estructuras de datos en memoria. No es una base de datos relacional: guarda pares clave-valor con una latencia típica inferior al milisegundo. Para Drupal, esto significa que las lecturas de caché dejan de competir por conexiones a MySQL y pasan a servirse desde RAM.
| Aspecto | Base de datos (MySQL) | Redis |
|---|---|---|
| Latencia de lectura | 1-10 ms | < 1 ms |
| Conexiones concurrentes | Limitadas por pool | Miles simultáneas |
| Persistencia | Sí (InnoDB) | Opcional (RDB/AOF) |
| Uso principal en Drupal | Contenido, configuración | Caché, locks, sesiones |
El módulo de Drupal para Redis (drupal/redis) lleva años en desarrollo activo y lo usan más de 50.000 sitios. Proporciona backend de caché, locks, control de inundación y colas, con soporte para PhpRedis (extensión C, la más rápida), Predis (librería PHP) y Relay.
El problema real: la base de datos como cuello de botella
Antes de configurar Redis en nuestro portal institucional, cada petición de página generaba un promedio de 40-60 consultas a tablas de caché. En horario punta, con 200-300 usuarios concurrentes, la base de datos no daba abasto. Las métricas contaban una historia clara:
- Tiempo medio de generación de página: ~450 ms
- Consultas a
cache_renderpor petición: 15-20 - Consultas a
cache_datapor petición: 8-12 - Conexiones MySQL activas en hora punta: cerca del límite del pool
Después de migrar la caché a Redis, los números cambiaron drásticamente: tiempo medio de generación bajó a ~90 ms, las consultas a MySQL por petición se redujeron más de un 60%, y las conexiones activas dejaron de ser un problema. El cambio fue de arquitectura, no de código.
Instalar Redis en el servidor
1. Instalar Redis Server
En Ubuntu o Debian, la instalación es directa:
sudo apt update
sudo apt install redis-server
Configurar el servicio para que gestione systemd:
sudo systemctl restart redis-server
sudo systemctl enable redis-server
Verificar que responde:
redis-cli ping
# Respuesta esperada: PONG
2. Instalar la extensión PHP Redis
Drupal necesita la extensión PHP para comunicarse con Redis. PhpRedis es la extensión C oficial, significativamente más rápida que la alternativa Predis:
sudo apt install php-redis
sudo phpenmod redis
Reiniciar PHP-FPM para que cargue la extensión:
sudo systemctl restart php8.3-fpm
Verificar que la extensión está cargada:
php -m | grep redis
# Debe mostrar: redis
3. Configurar redis.conf para producción
Editar /etc/redis/redis.conf con los ajustes clave:
# Límite de memoria (ajustar al 70-80% de la RAM disponible para caché)
maxmemory 12500mb
# Política de evicción: solo expira claves con TTL
# LFU (Least Frequently Used) es mejor que LRU para patrones típicos de Drupal
maxmemory-policy volatile-lfu
# Desactivar snapshots RDB para Redis de solo caché (evita escrituras a disco)
save ""
# Escuchar solo en localhost
bind 127.0.0.1 ::1
# Gestionado por systemd
supervised systemd
Reiniciar Redis para aplicar los cambios:
sudo systemctl restart redis-server
Configurar Redis en Drupal
1. Instalar el módulo Redis
El módulo se instala con Composer y se habilita con Drush:
composer require drupal/redis
drush en redis
El módulo proporciona los servicios de caché, locks, control de inundación y un informe de rendimiento en /admin/reports/redis.
2. Configurar settings.php
La configuración recomendada es crear un archivo separado settings.redis.php e incluirlo desde settings.php. Esto mantiene la configuración de Redis aislada y facilita la gestión entre entornos.
En settings.php, añadir la inclusión:
// settings.php — incluir configuración de Redis si existe
if (file_exists($app_root . '/' . $site_path . '/settings.redis.php')) {
include $app_root . '/' . $site_path . '/settings.redis.php';
}
Crear sites/default/settings.redis.php con la configuración completa:
<?php
// settings.redis.php — backend de caché con Redis
// Usar Redis como backend de caché por defecto.
$settings['cache']['default'] = 'cache.backend.redis';
// Conexión a Redis.
$settings['redis.connection']['host'] = '127.0.0.1';
$settings['redis.connection']['port'] = 6379;
// $settings['redis.connection']['password'] = 'tu_contraseña';
// Compresión: reduce el tamaño de las entradas grandes con overhead mínimo de CPU.
$settings['redis_compress_length'] = 100;
// Incluir servicios de lock, flood control y cache tags checksum.
$settings['container_yamls'][] = 'modules/contrib/redis/example.services.yml';
$settings['container_yamls'][] = 'modules/contrib/redis/redis.services.yml';
// Usar Redis para el caché del contenedor (optimización del bootstrap).
$class_loader->addPsr4('Drupal\\redis\\', 'modules/contrib/redis/src');
$settings['bootstrap_container_definition'] = [
'parameters' => [],
'services' => [
'redis.factory' => [
'class' => 'Drupal\redis\ClientFactory',
],
'cache.backend.redis' => [
'class' => 'Drupal\redis\Cache\CacheBackendFactory',
'arguments' => ['@redis.factory', '@cache_tags_provider.container', '@serialization.phpserialize'],
],
'cache.container' => [
'class' => '\Drupal\redis\Cache\PhpRedis',
'factory' => ['@cache.backend.redis', 'get'],
'arguments' => ['container'],
],
'cache_tags_provider.container' => [
'class' => 'Drupal\redis\Cache\RedisCacheTagsChecksum',
'arguments' => ['@redis.factory'],
],
'serialization.phpserialize' => [
'class' => 'Drupal\Component\Serialization\PhpSerialize',
],
],
];
La directiva bootstrap_container_definition es importante: permite que el contenedor de servicios de Drupal se cargue desde Redis durante el bootstrap, antes de que los módulos estén disponibles. Sin esto, las primeras peticiones siguen golpeando la base de datos.
El archivo example.services.yml que se incluye configura los servicios de lock, flood control y cache tags checksum para usar Redis. Sin esta inclusión, esos servicios siguen usando la base de datos por defecto.
3. Estrategia de bins
No todos los bins de caché son iguales. Algunos se leen en cada petición y otros solo durante el bootstrap. La estrategia recomendada es enviar todo a Redis mediante $settings['cache']['default'], que ya está configurado en el archivo anterior.
| Bin | Frecuencia de uso | En Redis | Notas |
|---|---|---|---|
render | Alta (cada petición) | ✅ | Fragmentos HTML renderizados |
page | Alta (páginas anónimas) | ✅ | Caché de página completa |
dynamic_page_cache | Alta (páginas autenticadas) | ✅ | Caché dinámico |
data | Alta | ✅ | Datos generales |
config | Media (bootstrap) | ✅ | Configuración compilada |
bootstrap | Media (bootstrap) | ✅ | Datos de arranque |
discovery | Baja | ✅ | Plugins y anotaciones |
Con $settings['cache']['default'] apuntando a Redis, todos los bins van a Redis por defecto. No es necesario configurarlos uno por uno.
Si necesitas enviar un bin específico a la base de datos (por ejemplo, para depuración), puedes hacerlo así:
// settings.redis.php — forzar un bin específico a la base de datos
$settings['cache']['bins']['mi_bin'] = 'cache.backend.database';
El failsafe: qué pasa si Redis se cae
El módulo de Drupal Redis tiene un failsafe integrado: si Redis no está disponible, el módulo usa un backend de caché nulo. Drupal no se cae, simplemente opera sin caché. Las peticiones siguen funcionando, aunque más lentas.
Para la caché del contenedor (bootstrap), el bootstrap_container_definition también es inherentemente tolerante a fallos: si Redis no responde, Drupal regenera el contenedor desde cero.
En producción, lo importante es que Redis se recupere automáticamente:
# Asegurar que Redis se reinicie si falla
sudo systemctl enable redis-server
Monitorizar la disponibilidad de Redis con una herramienta externa (monit, Prometheus, o simplemente un cron que haga redis-cli ping). Si Redis cae y no se recupera, Drupal sigue funcionando pero con rendimiento degradado.
Verificar que funciona
Panel de administración de Drupal
Visitar /admin/reports/redis. Este informe, que incluye el módulo, muestra:
- Cliente en uso (PhpRedis, Predis o Relay)
- Versión de Redis conectada
- Número de claves por bin
- Memoria configurada y política de evicción
- Ratio de lectura/escritura
Monitorización en tiempo real
Con redis-cli puedes ver las operaciones que Drupal está enviando a Redis:
redis-cli MONITOR
Navegar por el sitio y verás comandos HGETALL, SET, GET con prefijo drupal.. Si ves actividad, Redis está funcionando.
Comprobación rápida con Drush
# Informe del módulo Redis (requiere módulo 2.x)
drush redis:report
# Verificar que hay claves en Redis
redis-cli INFO keyspace
# Respuesta esperada: db0:keys=N,...
Errores comunes
| Error | Consecuencia | Solución |
|---|---|---|
No configurar maxmemory | Redis consume toda la RAM, el sistema lo mata | Añadir maxmemory en redis.conf |
Usar noeviction como política | Drupal recibe errores al escribir caché | Usar volatile-lfu o volatile-lru |
Usar allkeys-lru | Puede expirar locks y cache tags sin TTL | Usar volatile-lfu (solo expira con TTL) |
| Activar RDB snapshots para caché | Escrituras a disco innecesarias | Añadir save "" en redis.conf |
Olvidar example.services.yml | Locks y flood control siguen en BD | Incluir el YAML en settings |
No incluir bootstrap_container_definition | Bootstrap sigue golpeando la BD | Incluir el bloque completo |
| Usar Predis en producción | Rendimiento inferior a PhpRedis | Instalar la extensión C php-redis |
Lecciones aprendidas
-
Mide antes y después. No configures Redis por intuición. Usa el módulo
database_loggingo el informe de Redis para medir el impacto real. En nuestro caso, las consultas a caché por petición bajaron de 40-60 a menos de 5. -
No todos los bins son iguales, pero todos van a Redis. La configuración con
$settings['cache']['default']es suficiente. No compliques la estrategia mandando bins a backends distintos salvo que tengas un motivo concreto. -
El failsafe ya está integrado, no lo inventes. El módulo de Drupal Redis cae a caché nulo si Redis no está disponible. No escribas wrappers personalizados: usa la extensión C, configura systemd para auto-reinicio, y monitoriza desde fuera.
-
Redis no es persistente por defecto en nuestro caso, y eso está bien. Para caché, la persistencia no es necesaria. Si Redis se reinicia, Drupal regenera la caché progresamente. Solo necesitas persistencia si usas Redis para colas o sesiones.
-
Configura la memoria antes de que falle. Sin
maxmemory, Redis crece hasta que el sistema operativo lo mata. Define un límite y una política de evicción desde el primer día. En producción,volatile-lfues la mejor opción para Drupal.
Configurar Redis en Drupal es uno de los cambios de mayor impacto que puedes hacer en un sitio con tráfico real. La diferencia no es sutil: pasar de consultas a base de datos a lecturas en memoria cambia la experiencia del usuario y la estabilidad del sistema. Si tu Drupal sigue usando la caché por defecto, estás pagando un impuesto en cada petición.
Si quieres profundizar en cómo Drupal organiza sus bins de caché a bajo nivel, tienes la guía de cache bins. Y si estás planteando una arquitectura headless, la guía de headless con Next.js cubre cómo optimizar el rendimiento del lado del frontend. Y si necesitas importar contenido desde un sistema externo, la guía de Migrate API te explica cómo hacerlo con JSON y drush.
Preguntas frecuentes
¿Qué es Redis y para qué se usa en Drupal?
Redis (Remote Dictionary Server) es un almacén de estructuras de datos en memoria que opera en el rango de microsegundos. En Drupal se integra como backend de caché, locks, control de inundación y colas, sustituyendo las tablas de caché de la base de datos. Esto evita que cada petición genere decenas de consultas a MySQL y permite servir las lecturas de caché desde RAM, con una diferencia de rendimiento inmediata y medible.
¿Cómo configurar Redis como backend de caché en Drupal?
Instala la extensión PHP Redis (PhpRedis) y el módulo `drupal/redis` con Composer. En `settings.php` incluye un archivo `settings.redis.php` que defina `$settings['cache']['default'] = 'cache.backend.redis'`, la conexión, la compresión y los service containers. Es clave incluir `example.services.yml` y `redis.services.yml` para locks y flood control, y el `bootstrap_container_definition` para que el bootstrap se cargue desde Redis.
¿Qué política de evicción debe usar Redis para Drupal?
La recomendada es `volatile-lfu`, que solo expira claves con TTL (Least Frequently Used). Se debe configurar siempre `maxmemory` (al 70-80% de la RAM disponible) para que Redis no consuma toda la memoria del sistema. Debes evitar `noeviction` porque Drupal recibe errores al escribir caché, y `allkeys-lru`, que puede expirar locks y cache tags sin TTL.
¿Qué pasa si Redis se cae en un sitio Drupal?
El módulo de Drupal Redis tiene un failsafe integrado: si Redis no está disponible, usa un backend de caché nulo y el sitio sigue funcionando, solo que más lento. El `bootstrap_container_definition` también es tolerante a fallos y regenera el contenedor desde cero. En producción conviene activar el auto-reinicio con systemd y monitorizar la disponibilidad desde fuera, ya que si Redis no se recupera el rendimiento queda degradado.
¿Cómo verificar que Redis está funcionando en Drupal?
Puedes visitar el informe `/admin/reports/redis`, que muestra el cliente en uso, la versión de Redis, el número de claves por bin y la política de evicción. Con `redis-cli MONITOR` ves las operaciones que envía Drupal con prefijo `drupal.`. Desde drush puedes ejecutar `drush redis:report`, y con `redis-cli INFO keyspace` comprobar que hay claves almacenadas.
¿PhpRedis o Predis para Drupal?
PhpRedis es la extensión C oficial y es significativamente más rápida, por lo que es la recomendada para producción. Se instala como extensión del servidor (`sudo apt install php-redis`). Predis es una librería PHP pura que no requiere extensión, útil si no puedes instalar extensiones, pero su rendimiento es inferior. Existe también Relay como alternativa optimizada.