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. Úselo al agregar caché, un bloqueo distribuido, limitación de tasa o pub/sub con Redis, o cuando el diseño de claves necesite revisión.

¿Qué es patrones-redis?

Este skill proporciona una guía completa de referencia rápida para las mejores prácticas de Redis en casos de uso comunes de backend. Cubre desde el uso básico de estructuras de datos como strings, hashes, listas, conjuntos y conjuntos ordenados, hasta patrones avanzados como caché-aside (carga diferida), write-through, invalidación de caché basada en etiquetas, almacenamiento de sesiones y limitación de tasa mediante ventanas fijas y deslizantes con Lua. También incluye implementaciones robustas de bloqueos distribuidos con SET NX PX y el algoritmo Redlock para entornos multi-nodo, así como la diferenciación entre Pub/Sub (sin garantías) y Redis Streams (cola durable con grupos de consumidores). El skill detalla la gestión de conexiones con pooling, cluster y Sentinel, políticas de desalojo como allkeys-lru, y una sección de antipatrones que advierte sobre problemas comunes como claves sin TTL, uso de KEYS * o almacenamiento de blobs grandes. Diseñado para desarrolladores que trabajan con Redis en Python, el skill ofrece ejemplos prácticos, recomendaciones de nomenclatura y TTL, y enlaces a skills relacionados como patrones de PostgreSQL o Django. Es compatible con cualquier framework backend que use Redis y es ideal para auditar el diseño de claves o añadir funcionalidades de caché, bloqueos o mensajería.

Compatible con~Claude Code~Codex CLI✓Cursor
npx skills add https://github.com/affaan-m/ECC/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 en casos de uso comunes de backend.

Cómo Funciona

Redis es un almacén de estructuras de datos en memoria que admite strings, 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 múltiples pasos requieren scripts Lua, transacciones MULTI/EXEC o sincronización explícita para mantenerse atómicos. Los datos se persisten opcionalmente mediante instantáneas RDB o registros AOF. Los clientes se comunican a través de TCP usando el protocolo RESP; los pools de conexiones son esenciales para evitar la sobrecarga de establecer una conexión por cada petición.

Cuándo Activar

  • Agregar caché a una aplicación
  • Implementar limitación de tasa o estrangulamiento
  • 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ónSorted Setpuntuaciones:semanal
Visitantes únicosSetvisitantes:2024-01-01
Feed de actividadListfeed:usuario:456
Flujo de eventosStreameventos:pedidos
Contadores / límites de tasaString (INCR)limite_tasa:usuario:123
Filtro Bloom / HLLHyperLogLoghll:paginas_vistas

Patrones Principales

Caché-Aside (Carga Diferida)

import redis
import json

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

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

    if en_cache:
        return json.loads(en_cache)

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

Caché Write-Through

def actualizar_producto(producto_id: int, datos: dict):
    # Escribir en la BD primero
    db.execute("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: agrupa claves relacionadas bajo un conjunto
def cache_producto(producto_id: int, categoria_id: int, datos: dict):
    clave = f"producto:{producto_id}"
    etiqueta = f"etiqueta:categoria:{categoria_id}"
    tuberia = r.pipeline(transaction=True)
    tuberia.setex(clave, 3600, json.dumps(datos))
    tuberia.sadd(etiqueta, clave)
    tuberia.expire(etiqueta, 3600)
    tuberia.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}"
    tuberia = r.pipeline(transaction=True)
    tuberia.hset(clave, mapping={
        "usuario_id": usuario_id,
        "creado_en": int(time.time()),
    })
    tuberia.expire(clave, ttl)
    tuberia.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 se_limita_tasa(usuario_id: int, limite: int = 100, ventana: int = 60) -> bool:
    clave = f"limite_tasa:{usuario_id}:{int(time.time()) // ventana}"
    tuberia = r.pipeline(transaction=True)
    tuberia.incr(clave)
    tuberia.expire(clave, ventana)
    cuenta, _ = tuberia.execute()
    return cuenta > 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 seq = redis.call('INCR', clave_sec)
    redis.call('EXPIRE', clave_sec, math.ceil(ventana / 1000))
    redis.call('ZADD', clave, ahora, ahora .. '-' .. seq)
    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"limite_tasa: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 if 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 multi-nodo use la biblioteca redlock-py que implementa el algoritmo Redlock completo.

Pub/Sub y Streams

Pub/Sub (Fire-and-Forget)

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

# Suscriptor (bloqueante — ejecutar en hilo/proceso separado)
def suscribirse_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 Durable)

# 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:limite_tasa:usuario:123

# Patrón: recurso:fecha (claves con límite temporal)
estadisticas:paginas_vistas:2024-01-01

Estrategia de TTL

Tipo de DatoTTL Sugerido
Sesión de usuario24h (86400)
Caché de respuesta de 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

Establezca siempre un TTL. Las claves sin TTL se acumulan indefinidamente y causan presión sobre la 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('mymaestro', decode_responses=True)
replica = sentinel.slave_for('mymaestro', decode_responses=True)

Políticas de Desalojo

PolíticaComportamientoMejor Para
noevictionError al escribir cuando está llenoColas / datos críticos
allkeys-lruDesaloja el menos usado recientementeCaché general
volatile-lruLRU solo entre claves con TTLAlmacén de datos mixto
allkeys-lfuDesaloja el menos usado frecuentementePatrones de acceso sesgado
volatile-ttlDesaloja la que expira antesPriorizar datos de larga duración

Configure mediante redis.conf: maxmemory-policy allkeys-lru

Antipatrones

AntipatrónProblemaSolución
Claves sin TTLLa memoria crece sin límiteEstablezca siempre TTL
KEYS * en producciónBloquea el servidor (O(N))Use el cursor SCAN
Almacenar blobs grandes (>100KB)Serialización lenta, presión de memoriaAlmacene referencia + obtenga del almacén de objetos
Redis único para todoSin aislamiento entre caché y colaUse BD o instancias separadas
Ignorar límites del pool de conexionesAgotamiento de conexiones bajo cargaAjuste el pool a la carga de trabajo
No manejar estampida de fallos de cachéEmbudo en arranque en fríoUse bloqueos o caducidad temprana probabilística
FLUSHALL sin pensarloBorra toda la instanciaLimite las eliminaciones por patrón de clave

Prevención de Estampida de Fallos de Caché

import threading

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

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

    with _bloqueos_mutex:
        if clave not in _bloqueos:
            _bloqueos[clave] = threading.Lock()
        bloqueo = _bloqueos[clave]
    with bloqueo:
        en_cache = r.get(clave)  # Revisar de nuevo después de adquirir el bloqueo
        if en_cache:
            return json.loads(en_cache)
        valor = funcion_obtener()
        r.setex(clave, ttl, json.dumps(valor))
        return valor

Nota: para despliegues 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 caché-aside con setex y un TTL de 5 minutos en la respuesta. Clave según 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 estrangulamiento preciso por usuario.

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

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

Referencia Rápida

PatrónCuándo Usar
Caché-asideCarga pesada de lectura, tolera ligera obsolescencia
Write-throughSe requiere consistencia fuerte
Bloqueo distribuidoEvitar acceso concurrente a un recurso
Límite de tasa de ventana deslizanteEstrangulamiento preciso por usuario
Redis StreamsCola de eventos durable con grupos de consumidores
Pub/SubDifusión sin garantías de entrega necesarias
Tabla de clasificación con Sorted SetPuntuación clasificada, paginación
HyperLogLogConteo aproximado de únicos con bajo consumo de memoria

Relacionados

  • Skill: patrones-postgres — patrones de datos relacionales
  • Skill: patrones-backend — patrones de capa de API y 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 base 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/content-engine

Create platform-native content systems for X, LinkedIn, TikTok, YouTube, newsletters, and repurposed multi-platform campaigns. Use when the user wants social posts, threads, scripts, content calendars, or one source asset adapted cleanly across platforms.

affaan-m/ECC

Multi-platform content distribution across X, LinkedIn, Threads, and Bluesky. Adapts content per platform using content-engine patterns. Never posts identical content cross-platform. Use when the user wants to distribute content across social platforms.

affaan-m/fal-ai-media

Unified media generation via fal.ai MCP — image, video, and audio. Covers text-to-image (Nano Banana), text/image-to-video (Seedance, Kling, Veo 3), text-to-speech (CSM-1B), and video-to-audio (ThinkSound). Use when the user wants to generate images, videos, or audio with AI.

affaan-m/manim-video

日本語翻訳:このファイルは manim-video 用の日本語翻訳が必要です

affaan-m/remotion-video-creation

Remotion のベストプラクティス - React で動画を作成する。3D、アニメーション、音声、字幕、チャート、トランジションなどをカバーするドメイン固有の29のルール。

affaan-m/video-editing

AI-assisted video editing workflows for cutting, structuring, and augmenting real footage. Covers the full pipeline from raw capture through FFmpeg, Remotion, ElevenLabs, fal.ai, and final polish in Descript or CapCut. Use when the user wants to edit video, cut footage, create vlogs, or build video content.

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.

agent-self-evaluation

Use after completing any non-trivial task. The agent self-rates its output on 5 axes — accuracy, completeness, clarity, actionability, conciseness — with concrete evidence per criterion. Produces a structured 1-5 scorecard with specific improvement suggestions.

agent-sort

Build an evidence-backed ECC install plan for a specific repo by sorting skills, commands, rules, hooks, and extras into DAILY vs LIBRARY buckets using parallel repo-aware review passes. Use when ECC should be trimmed to what a project actually needs instead of loading the full bundle.

ai-first-engineering

Engineering operating model for teams where AI agents generate a large share of implementation output. Use when setting team process, review gates, or ownership rules for a codebase largely written by agents.

ai-regression-testing

Regression testing strategies for AI-assisted development. Sandbox-mode API testing without database dependencies, automated bug-check workflows, and patterns to catch AI blind spots where the same model writes and reviews code. Use when adding regression coverage to AI-assisted code, or when the same model both wrote and reviewed a change.

android-clean-architecture

Clean Architecture patterns for Android and Kotlin Multiplatform projects — module structure, dependency rules, UseCases, Repositories, and data layer patterns. Use when structuring modules, layers, or data flow in an Android or KMP project.

angular-developer

Generates Angular code and provides architectural guidance. Trigger when creating projects, components, or services, or for best practices on reactivity (signals, linkedSignal, resource), forms, dependency injection, routing, SSR, accessibility (ARIA), animations, styling (component styles, Tailwind CSS), testing, or CLI tooling.

Skills relacionados