Métodos de compresión en Redis y Valkey: cómo elegir el códec correcto (con comparativa práctica)

La compresión en bases de datos en memoria como Redis y Valkey puede marcar la diferencia entre un clúster que aguanta picos de tráfico con holgura y uno que se ahoga por latencia, CPU o memoria. Elegir bien el códec —y dónde aplicarlo— no es trivial: no es lo mismo comprimir valores “calientes” en caché que persistencia, ni HTML/JSON que binarios ya comprimidos (imágenes, ZIP, vídeo). Este artículo resume qué códecs son más útiles hoy (LZ4, Zstd, Gzip, Brotli, etc.), dónde aplicarlos en Redis y Valkey, y cuándo cada opción sale a cuenta.


Dónde comprimir en Redis/Valkey (y dónde no)

Antes de ver códecs, es clave entender las capas en las que suele aplicarse compresión:

  1. Valores (application/client-side)
    • Es el patrón más común en producción: comprimir el payload (JSON, HTML, blobs) en el cliente antes de SET/HSET, y descomprimir tras GET.
    • Pros: control total (eliges códec y nivel), fácil “feature flag”, ahorra RAM y ancho de banda.
    • Contras: CPU en la aplicación, y latencia extra si te pasas de nivel.
  2. Persistencia (RDB/AOF)
    • Redis/Valkey pueden comprimir snapshots (RDB) (históricamente con LZF) y usar AOF (apéndice) que puede comprimirse fuera de banda.
    • Pros: menor huella en disco y tiempos de backup más cortos.
    • Contras: no afecta a la memoria en caliente; cuidado con costes de CPU al guardar/cargar.
  3. Tráfico de red
    • Lo habitual es no comprimir RESP a nivel de protocolo en producción (TLS ya añade penalización y la compresión a nivel transporte no suele compensar).
    • Si hay WAN o inter-DC, considera proxying con compresión selectiva (solo objetos grandes).

Regla práctica: comprime en el cliente si el valor supera un umbral (p. ej., > 1–2 KiB) y es altamente repetitivo (JSON/HTML/CSV/Protobuf), con LZ4 si la latencia es crítica o Zstd si buscas mayor ratio con coste razonable.


Tabla comparativa de códecs habituales

Ratios orientativos en cargas tipo texto (JSON/HTML/logs). Con binarios ya comprimidos (JPEG, MP4, ZIP) el ratio cae y a menudo conviene no comprimir.

MétodoRatio estimadoEjemplo prácticoVelocidadEscenario recomendado
Identity1× (sin compresión)Binario 10 MB → 10 MBMáximaDatos no comprimibles, latencia ultra-baja
LZ41,10× – 1,20×Texto 10 MB → 8,5–9 MBExtremadamente altaCaches con mucha concurrencia (WordPress, APIs)
Zstd1,40× – 1,50×Texto 10 MB → 6,5–7 MBMuy altaCaché profesional, mejor balance “espacio/vel.”
Gzip1,35× – 1,45×Texto 10 MB → 7–7,5 MBAltaCompatibilidad universal
Brotli1,45× – 1,60×Web assets 10 MB → 6–7 MBBaja–mediaOptimización web/HTTP (no clave en caché RAM)
Bzip21,50× – 1,60×Texto 10 MB → 6,2–6,6 MBMuy bajaBackups/almacenamiento frío
LZF1,10× – 1,15×Texto 10 MB → 8,7–9 MBExtremadamente altaLegacy/compatibilidad; ganancias modestas
LZMA1,60× – 2,0×Texto 10 MB → 5–6,5 MBMuy bajaArchivos offline, espacio por encima de todo
Zlib1,35× – 1,45×Texto 10 MB → 7–7,5 MBAltaPlataformas mixtas, entornos clásicos

Conclusión rápida:

  • LZ4 = “latencia primero”.
  • Zstd = “mejor equilibrio moderno”.
  • Gzip/Zlib = “compatibilidad universal”.
  • Brotli/LZMA/Bzip2 = “ratio máximo, no para tiempo real”.
Métodos de compresión en Redis y Valkey: cómo elegir el códec correcto (con comparativa práctica) | comparativa redis valkey
Métodos de compresión en Redis y Valkey: cómo elegir el códec correcto (con comparativa práctica)

Redis vs Valkey: ¿qué cambia realmente?

  • En entornos profesionales, la compresión de valores se hace en el cliente (middleware/SDK/proxy).
  • Redis y Valkey comparten ecosistema y soporte de códecs a través de clientes, middlewares y módulos. Las opciones más frecuentes en caché son lz4 y zstd; lzf se mantiene por compatibilidad o CPU muy limitada; none para latencia absoluta o datos ya comprimidos.
  • Valkey suele exponer una variedad amplia de compresores vía integraciones (p. ej., django-valkey), mientras que en Redis verás los mismos códecs en librerías/SDKs maduros. En la práctica, la elección técnica y los resultados de LZ4/Zstd son equivalentes en ambos.

Reglas de decisión (operativas)

  1. Define un umbral por tamaño
    • P. ej., no comprimir valores < 1 KiB; comprimir ≥ 2–4 KiB.
    • Aplica “identity” (sin compresión) si el content-type ya viene comprimido (JPEG, MP4, ZIP).
  2. Elige códec por objetivo
    • p99/p999 bajoLZ4 (nivel default): excelente throughput y latencia.
    • Ahorro de RAM/egress sin matar la CPU → Zstd (niveles 1–6; evitar 15+ salvo batch/offline).
    • Compatibilidad universalGzip/Zlib.
    • Almacenamiento frío/backupBrotli/LZMA/Bzip2 (fuera del plano crítico).
  3. Evita comprimir dos veces
    • Detecta Content-Encoding/magics de archivo o usa sniffing (GZIP header, ZIP EOCD, JPEG SOI).
  4. Observabilidad obligatoria
    • Mide ratio, tiempo de (des)compresión, p95/p99 del RTT Redis, uso de CPU.
    • Si p99 sube, baja nivel de Zstd o muévete a LZ4.

Ejemplos de integración

Python (Redis/Valkey) con Zstandard y umbral

import redis
import zstandard as zstd

r = redis.Redis(host="localhost", port=6379)

ZSTD_LEVEL = 3
THRESHOLD = 2048  # 2 KiB

cctx = zstd.ZstdCompressor(level=ZSTD_LEVEL)
dctx = zstd.ZstdDecompressor()

def kv_set(key: str, data: bytes):
    if len(data) >= THRESHOLD:
        compressed = cctx.compress(data)
        # Marcamos un prefijo para saber que está comprimido
        r.set(key, b"\x28ZSTD\x29" + compressed)
    else:
        r.set(key, data)

def kv_get(key: str) -> bytes | None:
    val = r.get(key)
    if not val:
        return None
    if val.startswith(b"\x28ZSTD\x29"):
        return dctx.decompress(val[len(b"\x28ZSTD\x29"):])
    return val
Lenguaje del código: PHP (php)

Node.js con LZ4 (latencia crítica)

const Redis = require("ioredis");
const lz4 = require("lz4");

const r = new Redis();
const THRESHOLD = 2048;
const MAGIC = Buffer.from([0x28, 0x4c, 0x5a, 0x34, 0x29]); // (LZ4)

function setLZ4(key, buf) {
  if (buf.length < THRESHOLD) return r.setBuffer(key, buf);
  const max = lz4.encodeBound(buf.length);
  const out = Buffer.allocUnsafe(MAGIC.length + max);
  MAGIC.copy(out, 0);
  const n = lz4.encodeBlock(buf, out, MAGIC.length);
  return r.setBuffer(key, out.subarray(0, MAGIC.length + n));
}

async function getLZ4(key) {
  const val = await r.getBuffer(key);
  if (!val) return null;
  if (val.subarray(0, MAGIC.length).equals(MAGIC)) {
    const src = val.subarray(MAGIC.length);
    // Sin tamaño original, usar frame o anteponer length si lo necesitas
    // Para simplicidad, almacenar LZ4 frame sería preferible aquí.
  }
  return val;
}
Lenguaje del código: JavaScript (javascript)

En producción es preferible usar frames (LZ4 frame/Zstd frame) que incluyen tamaño original y CRC.

Django-Valkey (config compresor)

VALKEY = {
    "BACKENDS": {
        "default": {
            "HOST": "127.0.0.1",
            "PORT": 6379,
            "DB": 0,
            "COMPRESSION": {
                "ALGORITHM": "zstd",     # "lz4", "zstd", "gzip", "none"...
                "LEVEL": 3,              # Ajusta según CPU/latencia
                "THRESHOLD": 2048,       # En bytes
                "HEADER": True           # Marca formato/versión
            }
        }
    }
}
Lenguaje del código: PHP (php)

Micro-bench: cómo evaluar en tu entorno

  1. Genera datasets representativos:
    • JSON (1–50 KiB), HTML, logs, blobs binarios (no comprimibles), y mezcla real de tu tráfico.
  2. Mide por códec y nivel:
    • Ratio (bytes_in/bytes_out), t_comp/t_decomp, RTT Redis p50/p95/p99, CPU app/host.
  3. Prueba con THRESHOLD = {1 KiB, 2 KiB, 4 KiB} y picos de QPS (lecturas 80–95 % típico de caché).
  4. Repite con LZ4 (default) y Zstd (niveles 1–6).
    • Si el p99 sube > 10–15 % frente a “identity”, baja nivel o cambia a LZ4.
    • Si RAM es el cuello, sube Zstd a 3–5 y valida CPU.

Casos de uso típicos (qué elegir)

  • WordPress/HTML/API cache con QPS altoLZ4 (o Zstd-1 si p99 lo permite).
  • Catálogos JSON/Protobuf con TTL largosZstd-3/4 (ahorra RAM y egress).
  • Eventos/logs en listas/streamsLZ4 (ingesta masiva, baja latencia).
  • Backups/snapshots (RDB/AOF fuera de plano) → Brotli/LZMA/Bzip2.
  • Binarios ya comprimidos (imágenes, ZIP) → Identity (no comprimir).

Antipatrones y gotchas

  • Over-compression: niveles altos de Zstd/LZMA ↑CPU y ↑p99 sin ganar apenas bytes.
  • Overhead en claves pequeñas: para valores < 1 KiB, el header + coste CPU suele perder.
  • Compresión doble: detectar formatos comprimidos y saltar.
  • Fragmentación: objetos “grandes” (> 512 KiB) pueden presionar el allocator; dividir en chunks si es necesario.
  • Composición con pipelines: comprimir en edge (API) pero no en jobs internos con payload ya comprimido.

Recomendaciones finales

  • Por defecto moderno: Zstd-3 con THRESHOLD 2–4 KiB y feature flag para volver a LZ4 si el p99 sube.
  • Latencia extrema: LZ4 frame, sin niveles exóticos.
  • Compatibilidad: Gzip/Zlib si el ecosistema lo requiere.
  • Persistencia: comprimir RDB y backups; deja AOF para flujos donde necesites replay con mínimas transformaciones.

Fuentes (lecturas recomendadas)

Suscríbete al boletín SysAdmin

Este es tu recurso para las últimas noticias y consejos sobre administración de sistemas, Linux, Windows, cloud computing, seguridad de la nube, etc. Lo enviamos 2 días a la semana.

¡Apúntate a nuestro newsletter!


– patrocinadores –

Noticias destacadas

– patrocinadores –

¡SUSCRÍBETE AL BOLETÍN
DE LOS SYSADMINS!

Scroll al inicio
×