Caché con Memcached: Configuración
Introducción
Memcached es un sistema de almacenamiento en caché de objetos en memoria distribuida de alto rendimiento diseñado para acelerar aplicaciones web dinámicas al aliviar la carga de la base de datos. Originalmente desarrollado por Brad Fitzpatrick para LiveJournal en 2003, Memcached se ha convertido en una de las soluciones de almacenamiento en caché más utilizadas, impulsando algunos de los sitios web más grandes del mundo, incluyendo Facebook, Twitter, YouTube y Wikipedia.
A diferencia de Redis, Memcached está diseñado exclusivamente para el almacenamiento en caché con una arquitectura más simple enfocada en velocidad y eficiencia. Proporciona tiempos de respuesta submilisegundo y puede manejar millones de solicitudes por segundo con mínima sobrecarga de CPU. La simplicidad de Memcached lo hace extremadamente confiable y fácil de escalar horizontalmente agregando más servidores al clúster.
Esta guía completa cubre la instalación de Memcached, optimización de configuración, estrategias de almacenamiento en caché, ajuste de rendimiento y ejemplos de implementación del mundo real. Aprenderás cómo aprovechar Memcached para reducir drásticamente la carga de la base de datos y mejorar los tiempos de respuesta de la aplicación en un 70-95%.
Comprendiendo Memcached
¿Qué es Memcached?
Memcached es un almacén de clave-valor en memoria para pequeños fragmentos de datos arbitrarios de resultados de llamadas a bases de datos, llamadas a APIs o renderizado de páginas. Utiliza un diseño simple pero poderoso:
- Almacenamiento en Memoria: Todos los datos almacenados en RAM para acceso ultrarrápido
- Sistema Distribuido: Múltiples servidores forman un clúster
- Evicción LRU: Los elementos utilizados menos recientemente se eliminan cuando la memoria está llena
- Protocolo Simple: Protocolo basado en texto, fácil de implementar en clientes
- Sin Persistencia: Los datos se pierden al reiniciar (por diseño)
Memcached vs Redis
| Característica | Memcached | Redis |
|---|---|---|
| Uso Principal | Caché puro | Caché + almacén de datos |
| Estructuras de Datos | Simple clave-valor | Rico (strings, hashes, listas, sets, etc.) |
| Persistencia | Ninguna | Opcional (RDB/AOF) |
| Multi-threading | Sí | No (single-threaded) |
| Tamaño Máx. de Clave | 250 bytes | 512 MB |
| Tamaño Máx. de Valor | 1 MB (predeterminado) | 512 MB |
| Replicación | No (lado del cliente) | Sí (incorporada) |
| Clustering | Hash consistente del lado del cliente | Soporte nativo |
| Eficiencia de Memoria | Mejor para datos simples | Mejor para datos complejos |
Cuándo Elegir Memcached
Mejor Para:
- Escenarios de caché puro donde los datos pueden regenerarse
- Almacenamiento simple de datos clave-valor
- Aplicaciones que requieren rendimiento multi-threaded
- Escenarios donde no se necesita persistencia de datos
- Entornos con memoria limitada con datos simples
No Ideal Para:
- Estructuras de datos complejas
- Requisitos de almacenamiento persistente de datos
- Mensajería pub/sub
- Sets ordenados o listas
- Operaciones atómicas en datos complejos
Casos de Uso Comunes
- Caché de Consultas de Base de Datos: Almacenar resultados de consultas frecuentes
- Almacenamiento de Sesiones: Datos de sesión de usuario (con replicación de sesiones)
- Caché de Fragmentos de Página: Fragmentos HTML
- Caché de Objetos: Objetos de aplicación serializados
- Caché de Respuestas API: Resultados de llamadas a APIs externas
- Datos Calculados: Cálculos costosos
Instalación y Configuración
Instalando Memcached
# Ubuntu/Debian
apt-get update
apt-get install memcached libmemcached-tools -y
# CentOS/RHEL/Rocky Linux
dnf install memcached libmemcached -y
# Verificar instalación
memcached -h
memcached -V
Instalando Bibliotecas de Cliente
# Python
pip install python-memcached
# o pymemcache (más rápido)
pip install pymemcache
# PHP
apt-get install php-memcached
# Node.js
npm install memcached
# Ruby
gem install dalli
Configuración Básica
# Editar archivo de configuración
vim /etc/memcached.conf
# Configuraciones esenciales:
-d # Ejecutar como daemon
-m 64 # Límite de memoria (MB)
-p 11211 # Puerto
-u memcache # Ejecutar como usuario
-l 127.0.0.1 # Dirección de escucha (localhost)
-c 1024 # Máximo de conexiones simultáneas
-t 4 # Threads (número de núcleos de CPU)
-v # Modo verbose (para depuración)
# Para producción (4GB RAM, 4 núcleos):
-d
-m 4096
-p 11211
-u memcache
-l 127.0.0.1
-c 10240
-t 4
Iniciando Memcached
# Iniciar con systemd
systemctl start memcached
systemctl enable memcached
systemctl status memcached
# Verificar que Memcached está corriendo
echo "stats" | nc localhost 11211
# O con telnet
telnet localhost 11211
stats
quit
# Probar conexión con memcached-tool
memcached-tool localhost:11211 stats
Pruebas de Rendimiento de Memcached
Prueba de Rendimiento Base
# Instalar herramienta de benchmarking
git clone https://github.com/RedisLabs/memtier_benchmark.git
cd memtier_benchmark
autoreconf -ivf
./configure
make
make install
# Ejecutar benchmark
memtier_benchmark -s localhost -p 11211 --protocol=memcache_text \
-t 4 -c 50 -n 10000 --ratio=1:10 --data-size=1024
# Resultados típicos (hardware moderno, valores 1KB):
# Throughput: 150,000-300,000 ops/sec
# Latencia: 0.5-2ms promedio
# Operaciones GET: 95%+ en submilisegundo
# Operaciones SET: 90%+ en submilisegundo
Tiempo de Respuesta Base de la Aplicación
Sin Caché:
# Medir tiempo de consulta a base de datos
time curl http://localhost/api/products
# Resultados:
# Tiempo de respuesta: 380ms
# Consultas a base de datos: 12
# Carga de base de datos: 85% CPU
Con Memcached (después de la implementación):
time curl http://localhost/api/products
# Resultados:
# Tiempo de respuesta: 22ms (94% de mejora)
# Consultas a base de datos: 0.6 promedio (95% tasa de aciertos de caché)
# Carga de base de datos: 12% CPU (86% reducción)
Configuración y Optimización de Memoria
Asignación de Memoria
# Establecer límite de memoria en /etc/memcached.conf
-m 4096 # 4GB
# O iniciar con línea de comandos
memcached -m 4096 -d
# Calcular memoria recomendada:
# RAM Total: 16GB
# Sistema/OS: -2GB (reservar)
# Aplicaciones: -4GB
# Otros servicios: -2GB
# Disponible para Memcached: 8GB
-m 8192
Monitoreo de Uso de Memoria
# Conectar y verificar estadísticas
echo "stats" | nc localhost 11211 | grep -E "bytes|limit_maxbytes|evictions"
# Métricas clave:
# bytes: Uso actual de memoria
# limit_maxbytes: Memoria máxima permitida
# evictions: Número de elementos expulsados por presión de memoria
# Ejemplo de salida:
# STAT bytes 1073741824 # 1GB usado
# STAT limit_maxbytes 4294967296 # 4GB límite
# STAT evictions 12543 # Elementos expulsados
Script de Monitoreo
#!/bin/bash
# /usr/local/bin/memcached-monitor.sh
HOST="localhost"
PORT="11211"
echo "=== Monitor de Memcached - $(date) ==="
# Obtener estadísticas
STATS=$(echo "stats" | nc $HOST $PORT)
# Analizar métricas clave
BYTES=$(echo "$STATS" | grep "^STAT bytes" | awk '{print $3}')
LIMIT=$(echo "$STATS" | grep "^STAT limit_maxbytes" | awk '{print $3}')
CURR_ITEMS=$(echo "$STATS" | grep "^STAT curr_items" | awk '{print $3}')
EVICTIONS=$(echo "$STATS" | grep "^STAT evictions" | awk '{print $3}')
GET_HITS=$(echo "$STATS" | grep "^STAT get_hits" | awk '{print $3}')
GET_MISSES=$(echo "$STATS" | grep "^STAT get_misses" | awk '{print $3}')
# Calcular métricas
MEMORY_PCT=$(awk "BEGIN {printf \"%.2f\", ($BYTES/$LIMIT)*100}")
TOTAL_GETS=$((GET_HITS + GET_MISSES))
if [ $TOTAL_GETS -gt 0 ]; then
HIT_RATE=$(awk "BEGIN {printf \"%.2f\", ($GET_HITS/$TOTAL_GETS)*100}")
else
HIT_RATE="0"
fi
# Mostrar
echo "Uso de Memoria: $MEMORY_PCT%"
echo "Elementos en Caché: $CURR_ITEMS"
echo "Expulsiones: $EVICTIONS"
echo "Tasa de Aciertos de Caché: $HIT_RATE%"
# Alertar si hay muchas expulsiones
if [ $EVICTIONS -gt 10000 ]; then
echo "ADVERTENCIA: Alto conteo de expulsiones - considere aumentar la memoria" | \
mail -s "Alerta de Memcached" [email protected]
fi
chmod +x /usr/local/bin/memcached-monitor.sh
# Programar monitoreo
*/5 * * * * /usr/local/bin/memcached-monitor.sh >> /var/log/memcached-monitor.log
Configuración del Asignador Slab
Memcached utiliza asignación slab para la gestión de memoria:
# Ver estadísticas slab
echo "stats slabs" | nc localhost 11211
# Comprendiendo la asignación slab:
# Memcached divide la memoria en páginas de 1MB
# Las páginas se agrupan en slabs según el tamaño del elemento
# Factor de crecimiento predeterminado: 1.25
# Personalizar factor de crecimiento (más cercano a 1 = más clases slab, menos memoria desperdiciada)
memcached -f 1.10 -m 4096 -d
# Para tamaños de objeto variados, use un factor menor (1.10-1.15)
# Para tamaños de objeto uniformes, use un factor mayor (1.25-1.50)
# Ejemplo con configuración optimizada:
-d
-m 4096
-f 1.10 # Factor de crecimiento del 10%
-n 512 # Tamaño mínimo de asignación
-t 4
-c 10240
Configuración de Conexión y Threads
Límites de Conexión
# Establecer conexiones máximas en /etc/memcached.conf
-c 10240 # Predeterminado: 1024
# Calcular conexiones requeridas:
# Conexiones = (Servidores de aplicación × Conexiones por servidor × Margen de seguridad)
# Ejemplo: 5 servidores app × 200 conexiones × 1.5 = 1500 conexiones
-c 2048
# Monitorear conexiones actuales
echo "stats" | nc localhost 11211 | grep curr_connections
# Verificar si se alcanzó el máximo de conexiones
echo "stats" | nc localhost 11211 | grep listen_disabled_num
# Si > 0, aumentar límite de conexión
Configuración de Threads
# Establecer threads según núcleos de CPU
-t 4 # Para CPU de 4 núcleos
-t 8 # Para CPU de 8 núcleos
# NO sobre-threadear:
# Regla general: threads = núcleos de CPU
# Configurar demasiados threads perjudica el rendimiento
# Probar escalado de threads
# 1 thread
memcached -m 1024 -t 1 -p 11211 -d
memtier_benchmark -s localhost -p 11211 --protocol=memcache_text -t 4 -c 50
# 4 threads
memcached -m 1024 -t 4 -p 11211 -d
memtier_benchmark -s localhost -p 11211 --protocol=memcache_text -t 4 -c 50
# Resultados típicos:
# 1 thread: 80,000 ops/sec
# 4 threads: 280,000 ops/sec (3.5x mejora)
Configuración TCP
# Ajustar parámetros TCP del kernel para Memcached
cat >> /etc/sysctl.d/99-memcached.conf << 'EOF'
# Tamaños de buffer TCP
net.core.rmem_max = 16777216
net.core.wmem_max = 16777216
net.ipv4.tcp_rmem = 4096 87380 16777216
net.ipv4.tcp_wmem = 4096 65536 16777216
# Manejo de conexiones
net.core.somaxconn = 65535
net.core.netdev_max_backlog = 65536
net.ipv4.tcp_max_syn_backlog = 8192
# Optimización TIME_WAIT
net.ipv4.tcp_fin_timeout = 15
net.ipv4.tcp_tw_reuse = 1
# Cola de conexiones
net.ipv4.ip_local_port_range = 10000 65535
EOF
sysctl -p /etc/sysctl.d/99-memcached.conf
Configuración de Seguridad
Seguridad de Red
# Vincular a interfaz específica (predeterminado: todas las interfaces - INSEGURO)
-l 127.0.0.1 # Solo localhost (recomendado)
-l 192.168.1.100 # IP específica
-l 127.0.0.1,192.168.1.100 # Múltiples IPs
# Deshabilitar UDP (a menos que sea necesario)
-U 0 # Deshabilitar UDP completamente
# Ejemplo de configuración de producción:
-d
-m 4096
-l 127.0.0.1 # Solo localhost
-p 11211
-U 0 # Sin UDP
-c 10240
-t 4
-u memcache
Configuración de Firewall
# Solo si se necesita acceso remoto
# UFW (Ubuntu)
ufw allow from 192.168.1.0/24 to any port 11211 proto tcp
ufw enable
# Firewalld (CentOS/Rocky)
firewall-cmd --permanent --add-rich-rule='rule family="ipv4" source address="192.168.1.0/24" port protocol="tcp" port="11211" accept'
firewall-cmd --reload
# Iptables
iptables -A INPUT -p tcp -s 192.168.1.0/24 --dport 11211 -j ACCEPT
iptables -A INPUT -p tcp --dport 11211 -j DROP
iptables-save > /etc/iptables/rules.v4
Autenticación SASL
Habilitar SASL para autenticación:
# Instalar SASL
apt-get install sasl2-bin libsasl2-modules -y
# Crear directorio de configuración SASL
mkdir -p /etc/sasl2
cat > /etc/sasl2/memcached.conf << 'EOF'
mech_list: plain
sasldb_path: /etc/sasl2/memcached-sasldb2
EOF
# Crear usuario
echo "password" | saslpasswd2 -a memcached -c username -p
# Establecer permisos
chown memcache:memcache /etc/sasl2/memcached-sasldb2
chmod 640 /etc/sasl2/memcached-sasldb2
# Habilitar SASL en Memcached
# Agregar a /etc/memcached.conf:
-S # Habilitar SASL
# Reiniciar
systemctl restart memcached
# Probar con autenticación
memcached-tool localhost:11211 --user=username --password=password stats
Configuración de Memcached Distribuido
Hash Consistente del Lado del Cliente
Memcached utiliza distribución del lado del cliente:
Ejemplo Python:
from pymemcache.client.hash import HashClient
# Definir pool de servidores
servers = [
('10.0.1.10', 11211),
('10.0.1.11', 11211),
('10.0.1.12', 11211),
]
# Crear cliente con hash consistente
client = HashClient(servers, use_pooling=True)
# Las operaciones se distribuyen automáticamente
client.set('key1', 'value1') # Almacenado en servidor según hash de clave
client.set('key2', 'value2') # Puede estar en servidor diferente
value1 = client.get('key1') # Recuperado del servidor correcto
value2 = client.get('key2')
Ejemplo PHP:
<?php
$memcached = new Memcached();
// Agregar servidores
$memcached->addServers([
['10.0.1.10', 11211],
['10.0.1.11', 11211],
['10.0.1.12', 11211],
]);
// Habilitar hash consistente
$memcached->setOption(Memcached::OPT_DISTRIBUTION, Memcached::DISTRIBUTION_CONSISTENT);
$memcached->setOption(Memcached::OPT_LIBKETAMA_COMPATIBLE, true);
// Operaciones
$memcached->set('key1', 'value1');
$value1 = $memcached->get('key1');
?>
Estrategia de Escalado
# Comenzar con un solo servidor
Servidor 1: 8GB RAM, maneja 100% del tráfico
# Agregar servidores según sea necesario (el cliente maneja la distribución)
Servidor 1: 8GB RAM, maneja ~33% del tráfico
Servidor 2: 8GB RAM, maneja ~33% del tráfico
Servidor 3: 8GB RAM, maneja ~33% del tráfico
# Capacidad total: 24GB de caché distribuido
# Rendimiento: 3x (si está distribuido uniformemente)
# Mejores prácticas:
# 1. Agregar servidores en pares o múltiplos
# 2. Monitorear distribución entre servidores
# 3. Usar hash consistente para minimizar rehashing
# 4. Planificar para fallos de servidor (fallos de caché son aceptables)
Estrategias de Almacenamiento en Caché
Patrón Cache-Aside
Implementación más común:
import pymemcache.client.base as memcache
# Crear cliente
mc = memcache.Client(('localhost', 11211))
def get_user(user_id):
# Generar clave de caché
cache_key = f'user:{user_id}'
# Intentar obtener de caché
cached_user = mc.get(cache_key)
if cached_user:
# Acierto de caché
return json.loads(cached_user)
# Fallo de caché - consultar base de datos
user = database.query(f"SELECT * FROM users WHERE id = {user_id}")
# Almacenar en caché (TTL de 1 hora)
mc.set(cache_key, json.dumps(user), expire=3600)
return user
# Rendimiento:
# Acierto de caché: 1ms
# Fallo de caché: 95ms (consulta de base de datos)
# Con 95% de tasa de aciertos: promedio 5.75ms (vs 95ms sin caché)
Invalidación de Caché
def update_user(user_id, user_data):
# Actualizar base de datos
database.update(f"UPDATE users SET ... WHERE id = {user_id}", user_data)
# Invalidar caché
mc.delete(f'user:{user_id}')
# O actualizar caché inmediatamente (write-through)
# mc.set(f'user:{user_id}', json.dumps(user_data), expire=3600)
return user_data
Operaciones Multi-Get
Operaciones por lotes para mayor eficiencia:
def get_multiple_users(user_ids):
# Generar claves de caché
cache_keys = {f'user:{uid}': uid for uid in user_ids}
# Obtener de caché (una sola llamada de red)
cached_users = mc.get_many(cache_keys.keys())
# Identificar fallos
cached_ids = {cache_keys[k] for k in cached_users.keys()}
missing_ids = set(user_ids) - cached_ids
# Obtener faltantes de base de datos
if missing_ids:
db_users = database.query(
f"SELECT * FROM users WHERE id IN ({','.join(map(str, missing_ids))})"
)
# Cachear usuarios faltantes
cache_data = {f'user:{u["id"]}': json.dumps(u) for u in db_users}
mc.set_many(cache_data, expire=3600)
# Combinar resultados
all_users = list(cached_users.values()) + db_users
else:
all_users = list(cached_users.values())
return [json.loads(u) if isinstance(u, str) else u for u in all_users]
# Rendimiento:
# 100 usuarios sin lotes: 100 solicitudes de caché = ~100ms
# 100 usuarios con lotes: 1 solicitud de caché = ~2ms (50x más rápido)
Convenciones de Nomenclatura de Claves
# Usar nombres de clave estructurados
# Datos de usuario
user:{user_id} # Objeto de usuario
user:{user_id}:profile # Perfil de usuario
user:{user_id}:preferences # Preferencias de usuario
# Datos de sesión
session:{session_id}
# Resultados de consulta
query:{hash} # String de consulta hasheado
api:products:category:{cat_id} # Respuesta API
# Datos calculados
stats:daily:{date} # Estadísticas diarias
leaderboard:weekly # Tabla de clasificación semanal
# Claves basadas en tiempo
temp:{timestamp}:{id} # Datos temporales
# Usar prefijos para invalidación fácil
cache_v2:user:{user_id} # Prefijo de versión (cambiar para invalidar todo)
Ejemplos de Implementación del Mundo Real
Ejemplo 1: Caché de Objetos de WordPress
<?php
// wp-content/object-cache.php
class WP_Object_Cache {
private $memcache;
public function __construct() {
$this->memcache = new Memcached();
$this->memcache->addServer('localhost', 11211);
$this->memcache->setOption(Memcached::OPT_COMPRESSION, true);
$this->memcache->setOption(Memcached::OPT_BINARY_PROTOCOL, true);
}
public function get($key, $group = 'default') {
$cache_key = $this->buildKey($key, $group);
return $this->memcache->get($cache_key);
}
public function set($key, $data, $group = 'default', $expire = 0) {
$cache_key = $this->buildKey($key, $group);
return $this->memcache->set($cache_key, $data, $expire);
}
public function delete($key, $group = 'default') {
$cache_key = $this->buildKey($key, $group);
return $this->memcache->delete($cache_key);
}
private function buildKey($key, $group) {
return md5($group . $key);
}
}
$wp_object_cache = new WP_Object_Cache();
?>
<!-- Mejora de rendimiento:
Sin caché de objetos: 800ms carga de página, 45 consultas de base de datos
Con caché de objetos: 120ms carga de página, 3 consultas de base de datos (85% mejora)
-->
Ejemplo 2: Almacenamiento de Sesiones
from flask import Flask, session
from flask_session import Session
from pymemcache.client.base import Client
app = Flask(__name__)
# Configurar Memcached para sesiones
app.config['SESSION_TYPE'] = 'memcached'
app.config['SESSION_MEMCACHED'] = Client(('localhost', 11211))
app.config['SESSION_PERMANENT'] = False
app.config['SESSION_USE_SIGNER'] = True
app.config['SECRET_KEY'] = 'your-secret-key'
Session(app)
@app.route('/login', methods=['POST'])
def login():
# Sesión almacenada automáticamente en Memcached
session['user_id'] = user_id
session['username'] = username
return jsonify({'status': 'logged in'})
@app.route('/profile')
def profile():
# Sesión cargada automáticamente desde Memcached
user_id = session.get('user_id')
return jsonify({'user_id': user_id})
# Beneficios:
# - Sesiones compartidas entre múltiples servidores de aplicación
# - Acceso rápido a sesiones (< 1ms)
# - Expiración automática
# - Reducción de carga de base de datos
Ejemplo 3: Caché de Consultas de Base de Datos
import pymemcache.client.base as memcache
import hashlib
import json
from functools import wraps
mc = memcache.Client(('localhost', 11211))
def cache_query(ttl=3600):
def decorator(func):
@wraps(func)
def wrapper(*args, **kwargs):
# Crear clave de caché desde nombre de función y argumentos
key_data = f"{func.__name__}:{str(args)}:{str(kwargs)}"
cache_key = hashlib.md5(key_data.encode()).hexdigest()
# Intentar caché
cached_result = mc.get(cache_key)
if cached_result:
return json.loads(cached_result)
# Ejecutar consulta
result = func(*args, **kwargs)
# Almacenar en caché
mc.set(cache_key, json.dumps(result), expire=ttl)
return result
return wrapper
return decorator
# Uso
@cache_query(ttl=1800)
def get_popular_products(limit=10):
# Consulta costosa de base de datos
return db.execute("""
SELECT p.*, COUNT(o.id) as sales
FROM products p
JOIN orders o ON p.id = o.product_id
WHERE o.created_at > NOW() - INTERVAL 30 DAY
GROUP BY p.id
ORDER BY sales DESC
LIMIT %s
""", (limit,))
# Rendimiento:
# Primera llamada: 520ms (consulta de base de datos + almacenar caché)
# Llamadas cacheadas: 1.2ms (435x más rápido)
# Carga de base de datos reducida en 98% (con 98% tasa de aciertos de caché)
Ejemplo 4: Caché de Respuestas API
from flask import Flask, jsonify, request
import pymemcache.client.base as memcache
import json
import hashlib
app = Flask(__name__)
mc = memcache.Client(('localhost', 11211))
def cache_api_response(ttl=300):
def decorator(func):
@wraps(func)
def wrapper(*args, **kwargs):
# Crear clave de caché desde solicitud
cache_data = f"{request.path}:{request.query_string.decode()}"
cache_key = f"api:{hashlib.md5(cache_data.encode()).hexdigest()}"
# Intentar caché
cached_response = mc.get(cache_key)
if cached_response:
return jsonify(json.loads(cached_response))
# Generar respuesta
response_data = func(*args, **kwargs)
# Cachear respuesta
mc.set(cache_key, json.dumps(response_data), expire=ttl)
return jsonify(response_data)
return wrapper
return decorator
@app.route('/api/products')
@cache_api_response(ttl=600) # 10 minutos
def get_products():
category = request.args.get('category')
products = fetch_products_from_db(category)
return products
# Resultados:
# Sin caché: 340ms promedio, alta carga de base de datos
# Con caché (90% tasa de aciertos): 35ms promedio, 90% menos carga de base de datos
Ejemplo 5: Caché de Fragmentos
def render_product_list(category_id):
cache_key = f"fragment:product_list:{category_id}"
# Intentar obtener HTML cacheado
cached_html = mc.get(cache_key)
if cached_html:
return cached_html.decode('utf-8')
# Generar HTML
products = get_products(category_id)
html = render_template('product_list.html', products=products)
# Cachear fragmento (5 minutos)
mc.set(cache_key, html.encode('utf-8'), expire=300)
return html
# Rendimiento:
# Sin caché: 180ms (base de datos + renderizado de plantilla)
# Con caché: 2ms (99% mejora)
Monitoreo y Mantenimiento
Métricas Clave
#!/bin/bash
# Monitoreo completo de Memcached
echo "stats" | nc localhost 11211 | while read line; do
case "$line" in
*curr_items*) echo "$line" ;;
*total_items*) echo "$line" ;;
*bytes*) echo "$line" ;;
*curr_connections*) echo "$line" ;;
*total_connections*) echo "$line" ;;
*cmd_get*) echo "$line" ;;
*cmd_set*) echo "$line" ;;
*get_hits*) echo "$line" ;;
*get_misses*) echo "$line" ;;
*evictions*) echo "$line" ;;
*bytes_read*) echo "$line" ;;
*bytes_written*) echo "$line" ;;
esac
done
Cálculo de Tasa de Aciertos de Caché
#!/bin/bash
# Calcular tasa de aciertos de caché
STATS=$(echo "stats" | nc localhost 11211)
GET_HITS=$(echo "$STATS" | grep "get_hits" | awk '{print $3}')
GET_MISSES=$(echo "$STATS" | grep "get_misses" | awk '{print $3}')
TOTAL=$((GET_HITS + GET_MISSES))
if [ $TOTAL -gt 0 ]; then
HIT_RATE=$(awk "BEGIN {printf \"%.2f\", ($GET_HITS/$TOTAL)*100}")
echo "Tasa de Aciertos de Caché: $HIT_RATE%"
echo "Total Gets: $TOTAL"
echo "Aciertos: $GET_HITS"
echo "Fallos: $GET_MISSES"
# Alertar si tasa de aciertos es muy baja
HIT_RATE_INT=${HIT_RATE%.*}
if [ $HIT_RATE_INT -lt 80 ]; then
echo "ADVERTENCIA: Tasa de aciertos de caché por debajo del 80%" | \
mail -s "Alerta de Memcached" [email protected]
fi
fi
Análisis de Slab
# Analizar distribución de slab
echo "stats slabs" | nc localhost 11211
# Ver elementos en cada slab
echo "stats items" | nc localhost 11211
# Verificar desperdicio de slab
memcached-tool localhost:11211 display
Solución de Problemas
Alta Tasa de Expulsiones
# Verificar expulsiones
echo "stats" | nc localhost 11211 | grep evictions
# Causas:
# 1. Memoria muy pequeña - aumentar parámetro -m
# 2. TTLs muy largos - reducir tiempos de expiración
# 3. Problemas de asignación slab - ajustar factor de crecimiento
# Soluciones:
# Aumentar memoria
vim /etc/memcached.conf
# Cambiar: -m 4096 a -m 8192
# O optimizar TTLs
# Reducir de 3600 a 1800 para datos menos críticos
Errores de Conexión
# Verificar conexiones actuales
echo "stats" | nc localhost 11211 | grep curr_connections
# Verificar si se alcanzó el máximo
echo "stats" | nc localhost 11211 | grep maxconns
# Aumentar límite de conexión
vim /etc/memcached.conf
# Cambiar: -c 1024 a -c 10240
Fragmentación de Memoria
# Reiniciar Memcached para desfragmentar
systemctl restart memcached
# Programar reinicios regulares durante períodos de bajo tráfico
# Agregar a cron (3 AM diario):
# 0 3 * * * systemctl restart memcached
Conclusión
Memcached es una solución de almacenamiento en caché poderosa y eficiente que puede mejorar drásticamente el rendimiento de las aplicaciones. Una configuración e implementación adecuadas ofrecen beneficios sustanciales:
Mejoras de Rendimiento:
- Tiempos de respuesta: 70-95% de reducción
- Carga de base de datos: 80-95% de reducción
- Rendimiento: 10-50x de aumento
- Costos de infraestructura: 40-70% de reducción
Puntos Clave:
- Simple y rápido: Diseñado específicamente para caché
- Escalable horizontalmente: Agregar servidores para más capacidad
- Eficiente en memoria: La asignación slab minimiza el desperdicio
- Multi-threaded: Mejor utilización de CPU que alternativas single-threaded
Mejores Prácticas:
- Asignar suficiente memoria para mantener altas tasas de aciertos
- Usar TTLs apropiados según la volatilidad de los datos
- Implementar pooling de conexiones del lado del cliente
- Monitorear tasas de aciertos y expulsiones
- Usar hash consistente para configuraciones distribuidas
- Asegurar con restricciones de red (vincular a localhost o red privada)
Al implementar estas configuraciones y estrategias de Memcached, puedes construir aplicaciones de alto rendimiento y escalables con mínima complejidad y excelente confiabilidad.


