Communitygithub.com

patrones-redis

Patrones de estructuras de datos de Redis, estrategias de caché, bloqueos distribuidos, limitación de tasa, pub/sub y gestión de conexiones para aplicaciones en producción. Útil al agregar caché, un bloqueo distribuido, limitación de tasa o pub/sub con Redis, o cuando se necesita revisar el diseño de claves.

¿Qué es patrones-redis?

Este skill proporciona una guía completa de referencia rápida sobre las mejores prácticas de Redis para casos de uso comunes en backend. Cubre patrones esenciales como cache-aside (carga diferida), write-through (escritura directa), invalidación de caché basada en etiquetas y almacenamiento de sesiones. También incluye implementaciones detalladas de limitación de tasa con ventanas fijas y deslizantes (usando Lua para atomicidad), bloqueos distribuidos con el comando SET NX PX y un script Lua para liberación segura, así como pub/sub y Redis Streams para mensajería duradera. El skill aborda la gestión de conexiones con pooling, modo clúster y Sentinel para alta disponibilidad, junto con políticas de desalojo, convenciones de nomenclatura de claves, estrategias de TTL y una sección de antipatrones con soluciones como la prevención de estampidas de fallos de caché. Está diseñado para desarrolladores que trabajan con Python (usando la biblioteca redis-py) y necesitan implementar patrones robustos y escalables con Redis en producción.

Compatible con~Claude Code~Codex CLI✓Cursor
npx skills add https://github.com/affaan-m/everything-claude-code/tree/main/skills/redis-patterns

Preguntar en tu IA favorita

Abre un nuevo chat con esta habilidad de agente ya precargada.

Documentación

Patrones de Redis

Referencia rápida de las mejores prácticas de Redis para casos de uso comunes en backend.

Cómo funciona

Redis es un almacén de estructuras de datos en memoria que admite cadenas, hashes, listas, conjuntos, conjuntos ordenados, streams y más. Los comandos individuales de Redis son atómicos en una sola instancia; los flujos de trabajo de varios pasos requieren scripts Lua, transacciones MULTI/EXEC o sincronización explícita para mantener la atomicidad. Los datos se persisten opcionalmente mediante instantáneas RDB o registros AOF. Los clientes se comunican a través de TCP utilizando el protocolo RESP; los pools de conexiones son esenciales para evitar la sobrecarga del protocolo de enlace por solicitud.

Cuándo activar

  • Agregar caché a una aplicación
  • Implementar limitación de tasa o control de acceso
  • Construir bloqueos distribuidos o coordinación
  • Configurar almacenamiento de sesiones o tokens
  • Usar Pub/Sub o Redis Streams para mensajería
  • Configurar Redis en producción (pooling, desalojo, clustering)

Hoja de referencia de estructuras de datos

Caso de usoEstructuraClave de ejemplo
Caché simpleStringproducto:123
Sesión de usuarioHashsesion:abc
Tabla de clasificaciónConjunto ordenadopuntuaciones:semanal
Visitantes únicosConjuntovisitantes:2024-01-01
Feed de actividadListafeed:usuario:456
Flujo de eventosStreameventos:pedidos
Contadores / límites de tasaString (INCR)limitetasa:usuario:123
Filtro Bloom / HLLHyperLogLoghll:vistaspagina

Patrones principales

Cache-Aside (Carga diferida)

import redis
import json

r = redis.Redis(host='localhost', puerto=6379, decode_responses=True)

def obtener_producto(producto_id: int):
    clave_cache = f"producto:{producto_id}"
    cacheado = r.get(clave_cache)

    if cacheado:
        return json.loads(cacheado)

    producto = db.consulta("SELECT * FROM productos WHERE id = %s", producto_id)
    r.setex(clave_cache, 3600, json.dumps(producto))  # TTL: 1 hora
    return producto

Caché de escritura directa

def actualizar_producto(producto_id: int, datos: dict):
    # Escribir en la base de datos primero
    db.ejecutar("UPDATE productos SET ... WHERE id = %s", producto_id)

    # Actualizar caché inmediatamente
    clave_cache = f"producto:{producto_id}"
    r.setex(clave_cache, 3600, json.dumps(datos))

Invalidación de caché

# Invalidación basada en etiquetas: agrupar claves relacionadas bajo un conjunto
def cachear_producto(producto_id: int, categoria_id: int, datos: dict):
    clave = f"producto:{producto_id}"
    etiqueta = f"etiqueta:categoria:{categoria_id}"
    pipe = r.pipeline(transaction=True)
    pipe.setex(clave, 3600, json.dumps(datos))
    pipe.sadd(etiqueta, clave)
    pipe.expire(etiqueta, 3600)
    pipe.execute()

def invalidar_categoria(categoria_id: int):
    etiqueta = f"etiqueta:categoria:{categoria_id}"
    claves = r.smembers(etiqueta)
    if claves:
        r.delete(*claves)
    r.delete(etiqueta)

Almacenamiento de sesiones

import time
import uuid

def crear_sesion(usuario_id: int, ttl: int = 86400) -> str:
    sesion_id = str(uuid.uuid4())
    clave = f"sesion:{sesion_id}"
    pipe = r.pipeline(transaction=True)
    pipe.hset(clave, mapping={
        "usuario_id": usuario_id,
        "creado_en": int(time.time()),
    })
    pipe.expire(clave, ttl)
    pipe.execute()
    return sesion_id

def obtener_sesion(sesion_id: str) -> dict | None:
    datos = r.hgetall(f"sesion:{sesion_id}")
    return datos if datos else None

def eliminar_sesion(sesion_id: str):
    r.delete(f"sesion:{sesion_id}")

Limitación de tasa

Ventana fija (Simple)

def esta_limitado(usuario_id: int, limite: int = 100, ventana: int = 60) -> bool:
    clave = f"limitetasa:{usuario_id}:{int(time.time()) // ventana}"
    pipe = r.pipeline(transaction=True)
    pipe.incr(clave)
    pipe.expire(clave, ventana)
    contador, _ = pipe.execute()
    return contador > limite

Ventana deslizante (Lua — Atómico)

-- sliding_window.lua
local clave = KEYS[1]
local ahora = tonumber(ARGV[1])
local ventana = tonumber(ARGV[2])
local limite = tonumber(ARGV[3])

redis.call('ZREMRANGEBYSCORE', clave, 0, ahora - ventana)
local cuenta = redis.call('ZCARD', clave)

if cuenta < limite then
    -- Usar miembro único (ahora + secuencia) para evitar colisiones en el mismo milisegundo
    local clave_sec = clave .. ':seq'
    local sec = redis.call('INCR', clave_sec)
    redis.call('EXPIRE', clave_sec, math.ceil(ventana / 1000))
    redis.call('ZADD', clave, ahora, ahora .. '-' .. sec)
    redis.call('EXPIRE', clave, math.ceil(ventana / 1000))
    return 1
end
return 0
ventana_deslizante = r.register_script(open('sliding_window.lua').read())

def permitir_solicitud(usuario_id: int) -> bool:
    clave = f"limitetasa:deslizante:{usuario_id}"
    ahora = int(time.time() * 1000)
    return bool(ventana_deslizante(keys=[clave], args=[ahora, 60000, 100]))

Bloqueos distribuidos

Bloqueo distribuido (Nodo único — SET NX PX)

import uuid

def adquirir_bloqueo(recurso: str, ttl_ms: int = 5000) -> str | None:
    clave_bloqueo = f"bloqueo:{recurso}"
    token = str(uuid.uuid4())
    adquirido = r.set(clave_bloqueo, token, px=ttl_ms, nx=True)
    return token si adquirido else None

def liberar_bloqueo(recurso: str, token: str) -> bool:
    script_liberar = """
    if redis.call('get', KEYS[1]) == ARGV[1] then
        return redis.call('del', KEYS[1])
    else
        return 0
    end
    """
    resultado = r.eval(script_liberar, 1, f"bloqueo:{recurso}", token)
    return bool(resultado)

# Uso
token = adquirir_bloqueo("pedido:pago:123")
if token:
    try:
        procesar_pago()
    finally:
        liberar_bloqueo("pedido:pago:123", token)

Para configuraciones de varios nodos, use la biblioteca redlock-py que implementa el algoritmo Redlock completo.

Pub/Sub y Streams

Pub/Sub (Disparar y olvidar)

# Publicador
def publicar_evento(canal: str, carga: dict):
    r.publish(canal, json.dumps(carga))

# Suscriptor (bloqueante — ejecutar en hilo/proceso separado)
def suscribir_eventos(canal: str):
    pubsub = r.pubsub()
    pubsub.subscribe(canal)
    for mensaje in pubsub.listen():
        if mensaje['type'] == 'message':
            manejar(json.loads(mensaje['data']))

Redis Streams (Cola duradera)

# Productor
def emitir(stream: str, evento: dict):
    r.xadd(stream, evento, maxlen=10000)  # Limitar longitud del stream

# Grupo de consumidores — garantiza entrega al menos una vez
try:
    r.xgroup_create('eventos:pedidos', 'procesador', id='0', mkstream=True)
except Exception:
    pass  # El grupo ya existe

def consumir(stream: str, grupo: str, consumidor: str):
    while True:
        mensajes = r.xreadgroup(grupo, consumidor, {stream: '>'}, count=10, block=2000)
        for _, entradas in (mensajes or []):
            for msg_id, datos in entradas:
                procesar(datos)
                r.xack(stream, grupo, msg_id)

Prefiera Streams sobre Pub/Sub cuando necesite garantías de entrega, grupos de consumidores o reproducción.

Diseño de claves

Convenciones de nomenclatura

# Patrón: recurso:id:campo
usuario:123:perfil
pedido:456:estado
cache:producto:789

# Patrón: espacio_nombres:recurso:id
miapp:sesion:abc123
miapp:limitetasa:usuario:123

# Patrón: recurso:fecha (claves con límite de tiempo)
estadisticas:vistaspagina:2024-01-01

Estrategia de TTL

Tipo de datosTTL sugerido
Sesión de usuario24h (86400)
Caché de respuesta API5–15 min
Ventana de límite de tasaCoincidir con el tamaño de la ventana
Tokens de corta duración5–10 min
Tabla de clasificación1h–24h
Datos estáticos/de referencia1h–1 semana

Siempre establezca un TTL. Las claves sin TTL se acumulan indefinidamente y causan presión de memoria.

Gestión de conexiones

Pool de conexiones

from redis import ConnectionPool, Redis

pool = ConnectionPool(
    host='localhost',
    port=6379,
    db=0,
    max_connections=20,
    decode_responses=True,
    socket_connect_timeout=2,
    socket_timeout=2,
)

r = Redis(connection_pool=pool)

Modo clúster

from redis.cluster import RedisCluster

r = RedisCluster(
    startup_nodes=[{"host": "redis-1", "port": 6379}],
    decode_responses=True,
    skip_full_coverage_check=True,
)

Sentinel (Alta disponibilidad)

from redis.sentinel import Sentinel

sentinel = Sentinel(
    [('sentinel-1', 26379), ('sentinel-2', 26379)],
    socket_timeout=0.5,
)
maestro = sentinel.master_for('mimaestro', decode_responses=True)
replica = sentinel.slave_for('mimaestro', decode_responses=True)

Políticas de desalojo

PolíticaComportamientoMejor para
noevictionError al escribir cuando está llenoColas / datos críticos
allkeys-lruDesalojar el usado menos recientementeCaché general
volatile-lruLRU solo entre claves con TTLAlmacén de datos mixto
allkeys-lfuDesalojar el usado con menos frecuenciaPatrones de acceso sesgados
volatile-ttlDesalojar el que expira más prontoPriorizar datos de larga duración

Establecer en redis.conf: maxmemory-policy allkeys-lru

Antipatrones

AntipatrónProblemaSolución
Claves sin TTLLa memoria crece sin límiteSiempre establecer TTL
KEYS * en producciónBloquea el servidor (O(N))Usar cursor SCAN
Almacenar grandes blobs (>100KB)Serialización lenta, presión de memoriaAlmacenar referencia + obtener desde almacén de objetos
Un solo Redis para todoSin aislamiento entre caché y colaUsar bases de datos o instancias separadas
Ignorar límites del pool de conexionesAgotamiento de conexiones bajo cargaDimensionar el pool según la carga de trabajo
No manejar estampida de fallos de cachéAvalancha en inicio en fríoUsar bloqueos o caducidad temprana probabilística
FLUSHALL sin pensarLimpia toda la instanciaLimitar eliminaciones por patrón de clave

Prevención de estampida de fallos de caché

import threading

_bloqueos: dict[str, threading.Lock] = {}
_mutex_bloqueos = threading.Lock()

def obtener_con_bloqueo(clave: str, funcion_obtener, ttl: int = 300):
    cacheado = r.get(clave)
    if cacheado:
        return json.loads(cacheado)

    with _mutex_bloqueos:
        if clave not in _bloqueos:
            _bloqueos[clave] = threading.Lock()
        bloqueo = _bloqueos[clave]
    with bloqueo:
        cacheado = r.get(clave)  # Volver a verificar después de adquirir el bloqueo
        if cacheado:
            return json.loads(cacheado)
        valor = funcion_obtener()
        r.setex(clave, ttl, json.dumps(valor))
        return valor

Nota: para implementaciones multiproceso, reemplace el bloqueo en proceso con adquirir_bloqueo/liberar_bloqueo de la sección de Bloqueos distribuidos anterior.

Ejemplos

Agregar caché a un endpoint de API Django/Flask: Use cache-aside con setex y un TTL de 5 minutos en la respuesta. Clave basada en los parámetros de la solicitud.

Limitar la tasa de una API por usuario: Use ventana fija con pipeline(transaction=True) para endpoints de bajo tráfico; use Lua de ventana deslizante para una limitación precisa por usuario.

Coordinar un trabajo en segundo plano entre workers: Use adquirir_bloqueo con un TTL que supere la duración esperada del trabajo. Siempre libere en un bloque finally.

Distribuir notificaciones a múltiples suscriptores: Use Pub/Sub para disparar y olvidar. Cambie a Streams si necesita entrega garantizada o reproducción para consumidores tardíos.

Referencia rápida

PatrónCuándo usar
Cache-asideCarga pesada de lectura, tolera ligera desactualización
Escritura directaSe requiere consistencia fuerte
Bloqueo distribuidoPrevenir acceso concurrente a un recurso
Límite de tasa de ventana deslizanteLimitación precisa por usuario
Redis StreamsCola de eventos duradera con grupos de consumidores
Pub/SubTransmisión sin necesidad de garantías de entrega
Tabla de clasificación con conjunto ordenadoPuntuación clasificada, paginación
HyperLogLogConteo único aproximado con bajo consumo de memoria

Relacionados

  • Skill: patrones-postgres — patrones de datos relacionales
  • Skill: patrones-backend — patrones de API y capa de servicio
  • Skill: migraciones-base-datos — versionado de esquemas
  • Skill: patrones-django — integración del framework de caché de Django
  • Agente: revisor-base-datos — flujo de trabajo completo de revisión de bases de datos

Individual skills in this repo

This repo contains 20 individual skills — each has its own dedicated page.

accessibility

Design, implement, and audit inclusive digital products using WCAG 2.2 Level AA. Use when building or auditing UI that must meet WCAG 2.2 Level AA, or when reviewing a change for keyboard, contrast, or screen-reader support.

affaan-m/claude-api

Anthropic Claude API patterns for Python and TypeScript. Covers Messages API, streaming, tool use, vision, extended thinking, batches, prompt caching, and Claude Agent SDK. Use when building applications with the Claude API or Anthropic SDKs.

affaan-m/everything-claude-code

End-to-end marketing campaign planning and execution. Covers audience research, positioning, campaign angle definition, landing page copy, email sequences, social posts, ad copy, short-form video scripts, and content calendars. Use as the orchestration layer for multi-channel product launches. Use when planning or executing a multi-channel product launch, or producing landing page, email, social, or ad copy.

affaan-m/everything-claude-code

Development conventions and patterns for everything-claude-code. JavaScript project with conventional commits.

affaan-m/everything-claude-code-conventions

Development conventions and patterns for everything-claude-code. JavaScript project with conventional commits.

affaan-m/frontend-design

Create distinctive, production-grade frontend interfaces with high design quality. Use when the user asks to build web components, pages, or applications and the visual direction matters as much as the code quality.

affaan-m/gget

gget CLI and Python workflow for quick genomic database queries, sequence lookup, BLAST-style searches, enrichment checks, and reproducible bioinformatics evidence logs.

affaan-m/literature-review

Systematic literature-review workflow for academic, biomedical, technical, and scientific topics, including search planning, source screening, synthesis, citation checks, and evidence logging.

affaan-m/motion-ui

Production-ready UI motion system for React/Next.js. Use when implementing animations, transitions, or motion patterns.

affaan-m/project-guidelines-example

Example project-specific skill template based on a real production application.

affaan-m/pubmed-database

Direct PubMed and NCBI E-utilities search workflows for biomedical literature, MeSH queries, PMID lookup, citation retrieval, and API-backed literature monitoring.

affaan-m/scholar-evaluation

Structured scholarly-work evaluation for papers, proposals, literature reviews, methods sections, evidence quality, citation support, and research-writing feedback.

affaan-m/uspto-database

USPTO patent and trademark data workflow for official record lookup, PatentSearch queries, TSDR checks, assignment data, and reproducible IP research logs.

agent-architecture-audit

Full-stack diagnostic for agent and LLM applications. Audits the 12-layer agent stack for wrapper regression, memory pollution, tool discipline failures, hidden repair loops, and rendering corruption. Produces severity-ranked findings with code-first fixes. Essential for developers building agent applications, autonomous loops, or any LLM-powered feature. Use when an agent or LLM feature misbehaves and the failing layer is unknown, or before shipping an agent stack.

agent-eval

Head-to-head comparison of coding agents (Claude Code, Aider, Codex, etc.) on custom tasks with pass rate, cost, time, and consistency metrics. Use when choosing between coding agents, or when a change to an agent setup needs measured pass rate, cost, and time rather than an impression.

agent-harness-construction

Design and optimize AI agent action spaces, tool definitions, and observation formatting for higher completion rates. Use when defining or revising an agent

agentic-engineering

Operate as an agentic engineer using eval-first execution, decomposition, and cost-aware model routing. Use when planning or executing engineering work that agents will carry out end to end.

agentic-os

Build persistent multi-agent operating systems on Claude Code. Covers kernel architecture, specialist agents, slash commands, file-based memory, scheduled automation, and state management without external databases. Use when building a persistent multi-agent system on Claude Code with its own memory, commands, and scheduling.

agent-introspection-debugging

Structured self-debugging workflow for AI agent failures using capture, diagnosis, contained recovery, and introspection reports. Use when an agent run fails and you need a reproducible diagnosis instead of a retry.

agent-payment-x402

Add x402 payment execution to AI agents with per-task budgets, spending controls, and non-custodial wallets. Supports Base through agentwallet-sdk and X Layer through OKX Payments / OKX Agent Payments Protocol. Use when an agent must pay for something itself and needs per-task budgets, spending controls, and a non-custodial wallet.

Skills relacionados