From n00b to ZeroCool / Profesionalización

NoSQL: cuando las tablas ya no alcanzan (y tu backend ya pide auxilio)

Guía práctica para decidir y usar NoSQL: modelos, casos reales, diseño, consultas, índices, migración, errores comunes y checklist para producción.

Lo que vale la pena leer aquí

Te cae el ticket: “La app se tarda un chorro cuando el usuario abre su historial”. Tú, con tu laptop ya veterana, abres el profiler y lo primero que ves es una query con 6 JOINs, subqueries por todos lados y un GROUP BY que ya huele a deuda técnica. En staging “pasa”, pero en production —con datos de verdad y el tráfico de quincena— todo se arrastra.

Intro con gancho

El bug no era un bug. Era tu modelo.

Te cae el ticket: “La app se tarda un chorro cuando el usuario abre su historial”. Tú, con tu laptop ya veterana, abres el profiler y lo primero que ves es una query con 6 JOINs, subqueries por todos lados y un GROUP BY que ya huele a deuda técnica. En staging “pasa”, pero en production —con datos de verdad y el tráfico de quincena— todo se arrastra.

Y en la junta alguien dice la frase mágica: “¿Y si lo pasamos a NoSQL?”. Suena como escape hatch… hasta que te acuerdas del compa que lo intentó “en corto” y terminó con datos duplicados, un dashboard que mentía y un deploy nocturno con rollback incluido.

La neta: NoSQL no es para huir de SQL. Es para cuando tu problema ya no cabe bien en tablas.

Qué vas a aprender

  • Cómo detectar señales reales de que tus tablas ya no alcanzan (y cuándo no es NoSQL).
  • Las 4 familias más comunes de NoSQL y qué problema resuelven en backend.
  • Cómo modelar datos en NoSQL sin caer en “documentos monstruo” ni en “desnormalización sin control”.
  • Un paso a paso para elegir tecnología, diseñar el esquema, indexar y migrar sin romper production.
  • Errores que sí pasan en la vida real (y cómo salir del hoyo).

Contexto práctico: por qué existe NoSQL (y por qué a veces te conviene)

SQL es una joya cuando:

  • Tienes datos bien estructurados.
  • Necesitas integridad fuerte (constraints, transacciones claras).
  • Tus consultas cambian seguido y quieres flexibilidad para preguntar.

Pero en backend, cuando el producto crece, hay escenarios donde lo tabular ya va forzado.

Señales de que el modelo tabular ya está estirado

  1. Tu entidad principal ya es “un universo”: usuario → sesiones → eventos → dispositivos → features → flags → experiments → auditoría. En tablas, eso se vuelve un bosque de relaciones.
  2. Tu lectura manda: la app lee 50 veces por cada write, y esas lecturas piden “dámelo todo junto”, no “pásame pedacitos y yo armo”.
  3. La UI te dicta la forma del dato: feed, timeline, “mis pedidos con estatus y tracking”, “mi carrito con promos aplicadas”. Si cada pantalla dispara 8 queries, lo vas a sentir.
  4. Escalas por partición natural: por tenant, por usuario, por región. Si tu data se parte fácil, ciertas NoSQL se vuelven bien buena opción.

Señales de que NO necesitas NoSQL (todavía)

  • El problema real es un índice faltante, un N+1 o una consulta mal armada.
  • Estás usando JOINs por comodidad, pero podrías precomputar o cachear.
  • Necesitas reporteo pesado ad-hoc y consistencia estricta: ahí SQL suele ser tu compa.

Escena común en pyme/startup: “no hay presupuesto”, pero el costo fuerte no es la licencia; es la operación. Meter una NoSQL por hype y luego batallar con backups, monitoreo, sizing y restores… te puede salir más caro que arreglar tu Postgres bien hecho.

Guía principal: elegir, modelar y aterrizar NoSQL sin morir en el intento

1) Identifica tu patrón dominante: ¿qué estás optimizando?

Aquí no gana el diagrama bonito. Gana el patrón de acceso.

Preguntas que sí te aterrizan:

  • ¿Qué necesito leer en una sola pantalla o endpoint?
  • ¿Cuáles son mis queries top por tráfico y por costo?
  • ¿Qué parte necesita latencia baja sí o sí (checkout, login, feed)?
  • ¿Qué puedo tolerar que sea eventual (contador de likes, “última vez visto”)?

Ejemplo real (e-commerce, con jefe pidiendo cambios “para ayer”):

  • Lecturas: “pedido + items + shipping + tracking + pagos”.
  • Escrituras: cambios de estatus y eventos de tracking.
  • Necesitas auditoría y trazabilidad.

Eso casi siempre empuja a un enfoque tipo documento para el read model (pedido embebido) y, si te alcanza el stack, un write model con reglas más estrictas (o event-driven).

2) Conoce las familias NoSQL (sin romanticismo)

Document DB (MongoDB, Couchbase)

Útil cuando: tu unidad natural de lectura es un “documento” (pedido, perfil, carrito) y lo quieres leer casi completo.

  • Pros: lectura simple, modelado por agregados, flexible.
  • Contras: joins limitados, riesgo de duplicación, transacciones con matices.

Key-Value (Redis, DynamoDB tipo KV)

Útil cuando: necesitas latencia bajísima y acceso por llave.

  • Pros: rápido, ideal para caché, sesiones, rate limiting.
  • Contras: consultas complejas no son su fuerte.

Wide-column (Cassandra, ScyllaDB)

Útil cuando: ingestión masiva, time-series y acceso por partición + rango.

  • Pros: escala horizontal brutal, escrituras fuertes.
  • Contras: modelas por query sí o sí; no es para “preguntar lo que se me ocurra”.

Graph (Neo4j)

Útil cuando: relaciones profundas (recomendaciones, fraude, redes).

  • Pros: queries de relaciones súper naturales.
  • Contras: operación y skills menos comunes; no siempre escala como te lo imaginas.

Decisión con filo: si tu bronca es “quiero leer todo el pedido sin 10 JOINs”, Document DB suele ayudar. Si tu bronca es “necesito 2ms para sesiones y rate limit”, Redis. Si tu bronca es “telemetría de millones de eventos diarios con patrón fijo”, wide-column.

3) Modela por casos de uso (no por ERD)

Regla de oro NoSQL: diseña el modelo en función de las consultas.

Ejemplo: historial de eventos por usuario (alto volumen)

Consulta principal: “dame los últimos 50 eventos del usuario X, ordenados por fecha”.

Opción A: MongoDB (documentos por evento)

  • Colección events con campos: userId, ts, type, payload.
  • Índice compuesto: { userId: 1, ts: -1 }.

Opción B: Cassandra (tabla por partición)

  • Partición: userId
  • Clustering: ts DESC
  • Lectura súper eficiente.

Decisión práctica: si además quieres filtrar por campos dentro de payload (y no todo está bien estructurado), Mongo se siente natural. Si lo tuyo es volumen duro + patrones fijos, Cassandra te da paz.

4) Diseña tu estrategia de consistencia (porque lo eventual cobra)

NoSQL casi siempre te obliga a elegir tradeoffs:

  • Strong consistency (más simple mentalmente, a veces más cara/lenta)
  • Eventual consistency (más rápida/escalable, pero tienes que diseñar tolerancia)

Ejemplo cotidiano: contador de likes.

  • Eventual está bien: si tarda 2–5 segundos en reflejarse, nadie se muere.

Ejemplo peligroso: stock en checkout.

  • Ahí necesitas control fuerte (o un workflow de reservas) para no vender humo.

Truco de guerra: separa read model y write model.

  • Writes donde puedas garantizar reglas.
  • Reads donde necesitas velocidad.

5) Índices, tamaño de documentos y límites reales

MongoDB: evita el “documento Godzilla”

  • Si embebes demasiado, cada update puede reescribir más de lo que crees.
  • Si un arreglo crece sin control (tipo orders.items infinito), tarde o temprano pegas con límites o performance.

Regla que sí funciona:

  • Embebe lo que se lee junto y crece “acotado”.
  • Referencia lo que crece sin límite o se comparte mucho.

Redis: no es base de datos “porque sí”

  • Redis es una chulada… hasta que lo usas para persistir todo y te explota el costo de RAM.
  • Úsalo para caché, sesiones, colas ligeras, rate limiting, locks. Para lo demás, piénsalo dos veces.

6) Paso a paso de implementación (sin romper production)

Paso 0: Observabilidad primero

Antes de tocar datos:

  • Lista endpoints críticos.
  • Mide latencia p95/p99.
  • Mide throughput.
  • Identifica top queries y tamaños de payload.

Si no tienes baseline, no vas a saber si mejoraste o solo moviste el problema.

Paso 1: Elige un caso de uso acotado

No migres “toda la base” en un sprint. Agarra algo con impacto y bordes claros:

  • sesiones
  • historial de eventos
  • catálogo cacheado
  • feed/timeline

Paso 2: Diseña el modelo y valida con datos reales

Arma un dataset representativo.

  • No uses 100 registros. Usa miles o millones si aplica.
  • Prueba queries reales, con ordenamientos y filtros tal como los pide el endpoint.

Paso 3: Implementa dual-write o CDC (según tu realidad)

Opciones:

  • Dual-write (tu app escribe en SQL y NoSQL): rápido de montar, pero cuidado con consistencia si falla una de las escrituras.
  • CDC (Change Data Capture) de SQL hacia NoSQL: más robusto, más infraestructura.

Si eres equipo chico con deadline (y te cayó un freelance mal cotizado que no alcanza a hacer de todo): dual-write con colas y reintentos puede ser suficiente, pero deja claro cómo reconciliar cuando algo falle.

Snippet ejemplo (Node.js + MongoDB) para upsert de read model de pedido:

// Pseudocódigo: construir un documento optimizado para lectura
const orderReadModel = {
  _id: order.id,
  userId: order.userId,
  status: order.status,
  total: order.total,
  items: order.items.map(i => ({ sku: i.sku, qty: i.qty, price: i.price })),
  shipping: order.shipping,
  updatedAt: new Date()
};

await db.collection('orders_read').updateOne(
  { _id: order.id },
  { $set: orderReadModel },
  { upsert: true }
);

Paso 4: Lee desde NoSQL en “shadow mode”

  • Tu app sigue respondiendo con SQL.
  • En paralelo, haces la lectura NoSQL y comparas resultados (logs/metrics).
  • Ajustas modelo e índices sin meterle riesgo al usuario.

Paso 5: Cutover gradual + rollback claro

  • Activa lectura NoSQL por feature flag o por porcentaje.
  • Monitorea.
  • Ten un rollback sin drama: si algo huele raro, vuelves a SQL y sigues investigando.
NoSQL: cuando las tablas ya no alcanzan (y tu backend ya pide auxilio) - visual explicativa 1
Visual de apoyo: Intro con gancho

Screenshots sugeridos

  • Dashboard (Grafana/Datadog) con latencia p95 antes/después del cambio de lectura.
  • explain()/plan de consulta (MongoDB) validando el índice { userId: 1, ts: -1 }.
  • Configuración de TTL (Redis) para sesiones o caché.
  • Feature flag activando lectura NoSQL por porcentaje.

Errores comunes + solución

1) “Metimos NoSQL y ya no hay JOINs… pero ahora hay datos duplicados y nadie sabe cuál es la verdad”

Causa: desnormalización sin una fuente de verdad.
Solución: define un source of truth (SQL o un servicio) y usa NoSQL como read model/caché/derivado. Deja documentado cómo se regenera.

2) “Mongo está lento” (y resulta que no hay índices)

Causa: consultas por campos no indexados, o índices que no hacen match con el sort.
Solución: crea índices compuestos que reflejen la query real.

Ejemplo:

// Si haces: find({ userId }).sort({ ts: -1 })
// necesitas un índice que cubra ambos.
db.events.createIndex({ userId: 1, ts: -1 });

3) “Redis se tragó la RAM y el bill de la nube nos cacheteó”

Causa: guardar objetos enormes, sin TTL, o usar Redis como DB principal.
Solución: TTL obligatorio para caché/sesiones, límites de tamaño y políticas de eviction.

4) “Eventual consistency nos metió bugs fantasmas”

Causa: la UI/servicio asumía consistencia inmediata.
Solución: diseña estados intermedios (“procesando”, “pendiente”), idempotencia y reintentos. Para lo crítico, usa strong consistency o un workflow de reservas.

5) “La migración fue eterna porque intentamos mover todo”

Causa: scope sin control.
Solución: migra por caso de uso. Primero una lectura crítica, luego otra. Gana confianza y repite.

NoSQL: cuando las tablas ya no alcanzan (y tu backend ya pide auxilio) - visual explicativa 2
Visual de apoyo: Qué vas a aprender

Checklist final

  • Tengo claro el problema (latencia, escala, flexibilidad) y métricas base.
  • Elegí la familia NoSQL por patrón de acceso, no por hype.
  • Diseñé el modelo por consultas top (p95/p99), no por ERD.
  • Definí source of truth y acepto dónde hay duplicación.
  • Índices validados con explain()/equivalente y datos reales.
  • Estrategia de consistencia: qué es eventual y qué es fuerte.
  • Migración con shadow reads, feature flags y rollback.
  • Backups/restore probados y monitoreo listo.

FAQ

1) ¿NoSQL significa “sin esquema”?

No. Significa que el esquema no se impone como en SQL, pero igual existe: lo impones tú con validaciones, convenciones y pruebas. Si no, se vuelve un basurero con JSON bonito.

2) ¿Puedo usar NoSQL y SQL al mismo tiempo?

Sí, y muchas veces es lo más sano. Polyglot persistence: SQL para integridad y NoSQL para lecturas rápidas, caché o eventos. La clave es definir quién manda.

3) ¿MongoDB reemplaza a Postgres?

A veces, pero muchas veces lo complementa. Si tu negocio vive de reportes complejos y transacciones, Postgres sigue siendo top. Mongo brilla con agregados de lectura y estructuras cambiantes.

4) ¿Redis es NoSQL?

Sí (key-value), pero úsalo con respeto: es memoria. Perfecto para caché, sesiones, rate limit, locks, colas simples. No lo conviertas en tu “DB principal” sin entender el costo.

5) ¿Qué hago si ya estoy ahogado en JOINs pero no puedo migrar todo?

Ataca el cuello de botella más caro: crea un read model (documento) para ese endpoint. Dual-write o CDC, shadow reads, feature flag y listo. No necesitas quemar el monolito para mejorar latencia.

Siguiente episodio: teaser

La siguiente parada en el Backend Underworld: cachés y consistencia. Cómo cachear sin mentirte a ti mismo (y sin servir datos viejos cuando más importa).