Caching en JavaScript: Redis, Memcached y soluciones integradas (nivel avanzado)
En el desarrollo web moderno, el rendimiento de la aplicación a menudo se topa con la velocidad de acceso a los datos. Cada consulta adicional a la base de datos o API externa aumenta el tiempo de respuesta y la carga en el servidor. El caching no es simplemente "guardar el resultado", sino toda una ciencia que implica elegir la estrategia, herramienta y mecanismo de invalidación correctos. En este artículo, analizaremos técnicas avanzadas de caching en Node.js: desde soluciones integradas hasta sistemas industriales como Redis y Memcached.
1. Caching integrado en Node.js: simplicidad y limitaciones
Antes de conectar servicios externos, vale la pena evaluar las capacidades del propio Node.js. El caching integrado se implementa mediante objetos Map o WeakMap, así como variables globales simples. Esto es ideal para datos que cambian raramente y viven dentro de un solo proceso.
// Caché en memoria simple con TTL (Time To Live)class MemoryCache { constructor(ttl = 60000) { this.cache = new Map(); this.ttl = ttl; }
set(key, value) { this.cache.set(key, { value, expiry: Date.now() + this.ttl }); }
get(key) { const item = this.cache.get(key); if (!item) return null; if (Date.now() > item.expiry) { this.cache.delete(key); return null; } return item.value; }
delete(key) { this.cache.delete(key); }
clear() { this.cache.clear(); }}
// Usoconst cache = new MemoryCache(30000); // 30 segundos TTLcache.set('user:42', { name: 'Alice', role: 'admin' });console.log(cache.get('user:42')); // { name: 'Alice', role: 'admin' }Ventajas: latencia de red cero, simplicidad de implementación, no requiere dependencias externas.
Desventajas: los datos se pierden al reiniciar el proceso, no escala a múltiples servidores, está limitado por la memoria RAM de un solo nodo.
Para microservicios que funcionan en un solo proceso, o para almacenar en caché los resultados de cálculos pesados (por ejemplo, renderizado de plantillas), la solución integrada es una excelente opción. Sin embargo, para sistemas distribuidos no es suficiente.
2. Redis: el rey del caching en Node.js
Redis es un almacén de datos en memoria que se utiliza a menudo como un caché de alto rendimiento. Sus principales ventajas: soporte para estructuras de datos complejas (cadenas, listas, conjuntos, hashes), TTL incorporado, pub/sub y persistencia en disco.
Conexión de Redis en Node.js
Para trabajar con Redis en Node.js se utiliza el paquete ioredis (recomendado) o redis. Veamos un ejemplo con ioredis:
const Redis = require('ioredis');
const redis = new Redis({ host: 'localhost', port: 6379, retryStrategy: (times) => Math.min(times * 50, 2000) // estrategia de reconexión});
// Operaciones básicasawait redis.set('mykey', '¡Hola, Redis!', 'EX', 60); // TTL 60 segundosconst value = await redis.get('mykey');console.log(value); // "¡Hola, Redis!"
// Uso de hash para almacenar objetos complejosawait redis.hmset('user:100', { name: 'Bob', email: 'bob@example.com', age: 30});const user = await redis.hgetall('user:100');console.log(user); // { name: 'Bob', email: 'bob@example.com', age: '30' }Estrategias avanzadas con Redis
Cache-Aside (Lazy Loading): la aplicación primero verifica el caché, y solo en caso de fallo accede a la base de datos y llena el caché.
async function getUser(userId) { const cacheKey = `user:${userId}`; let user = await redis.get(cacheKey); if (user) { return JSON.parse(user); } // Fallo de caché — cargar desde la BD user = await db.findUserById(userId); if (user) { await redis.set(cacheKey, JSON.stringify(user), 'EX', 3600); // caché por 1 hora } ret