Modelado Orientado a Consultas: Rendimiento y Escalabilidad sin Perder Integridad

Capítulo 8

Tiempo estimado de lectura: 10 minutos

+ Ejercicio

Qué significa “modelar orientado a consultas”

El modelado orientado a consultas consiste en ajustar el modelo lógico (tablas, claves, restricciones y estructuras de acceso) para que las consultas críticas y las cargas de trabajo esperadas funcionen con buen rendimiento y escalen, sin romper reglas de negocio ni degradar la consistencia. No se trata de “optimizar una consulta”, sino de diseñar para patrones repetidos: lecturas frecuentes, filtros típicos, ordenamientos, agregaciones, y también para el costo de mantener esos datos cuando hay escrituras.

La idea central: cada decisión de rendimiento tiene un costo (más almacenamiento, más complejidad, más costo de escritura, más riesgo de inconsistencias). Por eso, el enfoque es controlado y documentado.

Anticipar consultas críticas y cargas de trabajo (guía práctica)

Paso 1: Inventario de consultas y SLAs

Antes de tocar el modelo, define un inventario de consultas críticas (las que más se ejecutan o las que deben responder rápido). Para cada consulta, captura:

  • Propósito (pantalla, API, reporte, proceso batch).
  • Frecuencia (por minuto/hora/día).
  • Volumen (filas esperadas hoy y en 12–24 meses).
  • Latencia objetivo (p95/p99 si aplica).
  • Patrón: filtros (WHERE), joins, ORDER BY, GROUP BY, paginación.

Ejemplo de ficha de consulta (formato sugerido):

Q-01: Listado de pedidos por cliente (últimos 90 días), ordenado por fecha desc, paginado 50/50. Frecuencia: alta (API). SLA: p95 < 200ms. Crecimiento: 10M pedidos/año.

Paso 2: Clasificar el tipo de carga

Clasifica el sistema (o cada módulo) según su mezcla de operaciones:

Continúa en nuestra aplicación.
  • Escuche el audio con la pantalla apagada.
  • Obtenga un certificado al finalizar.
  • ¡Más de 5000 cursos para que explores!
O continúa leyendo más abajo...
Download App

Descargar la aplicación

  • OLTP: muchas escrituras pequeñas, lecturas por clave, consistencia fuerte.
  • Lectura intensiva: muchas consultas repetidas, necesidad de cachés/vistas/índices.
  • Analítica/Reporting: agregaciones grandes, escaneos, ventanas temporales.

Esta clasificación ayuda a decidir cuánto “peso” puedes agregar al modelo (índices, materializaciones, tablas resumen) sin colapsar el rendimiento de escritura.

Paso 3: Identificar “rutas de acceso”

Para cada consulta crítica, define su ruta de acceso ideal: qué columna filtra primero, qué columnas ordenan, qué columnas se devuelven. Esto se traduce en decisiones de índices y, a veces, en denormalizaciones controladas.

Índices: relación con claves, selectividad y costo de escritura

Índices y claves: cómo se conectan

Conceptualmente, un índice es una estructura auxiliar que acelera búsquedas/ordenamientos. En el modelo lógico, se relaciona con:

  • Clave primaria (PK): normalmente crea (o se apoya en) un índice único. Excelente para búsquedas por identificador.
  • Claves únicas (UK): suelen requerir índices únicos para garantizar unicidad y acelerar búsquedas por “clave natural” o alternativa (por ejemplo, email).
  • Claves foráneas (FK): muchas veces conviene indexarlas para acelerar joins y validaciones de integridad referencial (especialmente en borrados/actualizaciones del padre).

Regla práctica: si una columna participa frecuentemente en joins o filtros, evalúa indexarla; si además debe ser única, el índice único aporta integridad y rendimiento a la vez.

Selectividad: cuándo un índice realmente ayuda

La selectividad mide qué tan bien un filtro reduce el conjunto de filas. Un índice suele ser más útil cuando el filtro devuelve un porcentaje pequeño del total (alta selectividad). Si filtras por un valor que aparece en muchísimas filas (baja selectividad), el motor puede preferir un escaneo.

Ejemplos típicos:

  • Alta selectividad: pedido_id, email, nro_documento.
  • Baja selectividad: estado = 'ACTIVO' si casi todos están activos.

Esto no significa que nunca indexes columnas de baja selectividad: pueden ser útiles en índices compuestos (por ejemplo, (cliente_id, estado, fecha)) donde la primera columna sí es selectiva.

Índices compuestos: orden, filtros y ORDER BY

Un índice compuesto sirve cuando la consulta filtra/ordena por varias columnas. El orden de columnas importa: en general, coloca primero las columnas más usadas en filtros y con buena selectividad, y luego las de ordenamiento.

Ejemplo conceptual (no dependiente de un motor específico):

Consulta: pedidos de un cliente en rango de fechas, ordenados por fecha desc, paginados. WHERE cliente_id = ? AND fecha >= ? ORDER BY fecha DESC

Un índice compuesto como (cliente_id, fecha) suele ayudar porque permite localizar rápidamente los pedidos del cliente y recorrerlos ya ordenados por fecha.

Costos de escritura: el “precio” de cada índice

Cada índice adicional implica trabajo extra en INSERT/UPDATE/DELETE:

  • Más escrituras físicas (actualizar estructura del índice).
  • Mayor uso de almacenamiento.
  • Posibles bloqueos/contención en cargas altas.

Criterio práctico: crea índices para consultas críticas y elimina/evita índices “por si acaso”. Mantén un registro de por qué existe cada índice (consulta que lo justifica y métrica esperada).

Checklist lógico para decidir un índice

  • ¿La columna aparece en WHERE/JOIN/ORDER BY de consultas críticas?
  • ¿La selectividad es razonable o forma parte de un índice compuesto útil?
  • ¿El índice ayuda a evitar un sort o un escaneo grande?
  • ¿El costo de escritura es aceptable para la carga OLTP?
  • ¿Puede reemplazarse por un índice existente (evitar duplicados)?

Vistas y “materialización conceptual”: qué resuelven y qué exigen

Vistas: encapsular consultas y estabilizar contratos

Una vista es una definición guardada de una consulta. A nivel de modelado, sirve para:

  • Reutilización: centralizar joins y reglas de filtrado (por ejemplo, “solo activos”).
  • Contrato estable: exponer un esquema lógico a consumidores sin revelar toda la complejidad.
  • Seguridad: limitar columnas/filas expuestas (según el motor y la estrategia de permisos).

Importante: una vista “normal” no almacena datos; su rendimiento depende de la consulta subyacente y de los índices de las tablas base.

Materialización conceptual: cuando “precomputar” tiene sentido

La materialización (vista materializada o estructura equivalente) consiste en almacenar el resultado precomputado de una consulta para acelerar lecturas repetidas, típicamente agregaciones o joins costosos.

Problemas que resuelve:

  • Dashboards con agregaciones frecuentes (ventas por día, por categoría).
  • Consultas con joins pesados sobre tablas grandes.
  • Reducción de carga en horas pico (sirviendo resultados ya preparados).

Exigencias en consistencia:

  • Frescura: ¿los datos pueden estar desfasados (segundos/minutos) o deben ser inmediatos?
  • Modo de actualización: refresco incremental vs refresco completo.
  • Gestión de fallos: qué pasa si el refresco falla; cómo se reintenta; cómo se monitorea.

En términos de reglas de negocio, la materialización no debe “inventar” estados: debe derivar de fuentes confiables y tener definido su nivel de consistencia aceptable.

Denormalizaciones típicas controladas

La denormalización orientada a consultas agrega redundancia deliberada para acelerar lecturas. Debe ser controlada: con criterios de aceptación, mecanismo de mantenimiento y documentación clara.

1) Campos agregados (contadores y totales)

Ejemplos:

  • cliente.total_pedidos
  • pedido.total_items
  • pedido.total_importe

Cuándo conviene: cuando el total se consulta muy a menudo y calcularlo en tiempo real (SUM/COUNT) sobre muchas filas es costoso.

Riesgo: desincronización si no se actualiza en cada cambio relevante.

2) Tablas resumen (hechos agregados por período/dimensión)

Ejemplos:

  • ventas_diarias(fecha, tienda_id, total_ventas, total_pedidos)
  • inventario_resumen(producto_id, stock_disponible, stock_reservado)

Cuándo conviene: reporting y paneles donde se consulta por fecha/dimensión repetidamente.

Decisión clave: granularidad (diaria, horaria) y dimensiones (por tienda, por categoría). A mayor granularidad, mayor costo de mantenimiento.

3) Copias de atributos para evitar joins (duplicación selectiva)

Ejemplo: guardar cliente_nombre en pedido para listados rápidos.

Cuándo conviene: cuando el join es muy frecuente y el atributo cambia poco, o cuando se necesita “congelar” el valor histórico (por ejemplo, nombre al momento de la compra).

Regla de negocio a definir: ¿es un dato “actual” o “histórico”? Si es histórico, no debe actualizarse cuando cambie el cliente.

Mecanismos de mantenimiento: triggers, jobs y lógica de aplicación

Opción A: Triggers (sincronización inmediata)

Un trigger actualiza el dato denormalizado en la misma transacción (o en el mismo evento) que modifica la tabla base.

Ventajas:

  • Consistencia fuerte (menos desfase).
  • Centraliza la regla en la base de datos.

Costos/precauciones:

  • Mayor latencia de escritura.
  • Complejidad de depuración.
  • Riesgo de cascadas de triggers y contención.

Ejemplo conceptual: mantener pedido.total_items al insertar/eliminar renglones en pedido_item.

Opción B: Jobs/Procesos batch (consistencia eventual controlada)

Un job recalcula o ajusta resúmenes cada cierto intervalo (cada minuto/hora/noche).

Ventajas:

  • Reduce costo en transacciones OLTP.
  • Permite recomputar masivamente y corregir desvíos.

Costos/precauciones:

  • Datos pueden estar desactualizados entre ejecuciones.
  • Necesita monitoreo, reintentos y control de ventanas de tiempo.

Patrón útil: tabla resumen diaria recalculada incrementalmente por partición temporal (por ejemplo, “solo hoy” y “ayer”).

Opción C: Lógica de aplicación (servicio como fuente de verdad operativa)

La aplicación actualiza tanto la tabla base como la denormalizada.

Ventajas:

  • Control explícito en el flujo de negocio.
  • Más fácil de versionar junto con el código.

Costos/precauciones:

  • Riesgo de inconsistencias si hay múltiples servicios escribiendo.
  • Requiere transacciones bien definidas y pruebas robustas.

Recomendación: si eliges esta opción, define claramente el “dueño” del dato denormalizado (un único servicio) y cómo se evita la doble escritura no coordinada.

Criterios de aceptación para optimizaciones orientadas a consultas

Antes de introducir un índice, vista materializada o denormalización, define criterios verificables:

  • Consulta objetivo: identificador (Q-01, Q-02) y SQL/pseudoconsulta asociada.
  • Métrica esperada: latencia p95/p99, CPU, lecturas lógicas, filas examinadas.
  • Impacto en escritura: incremento máximo aceptable de latencia en INSERT/UPDATE/DELETE.
  • Consistencia: inmediata vs eventual; tolerancia de desfase (por ejemplo, 60s).
  • Plan de rollback: cómo desactivar el índice/materialización si empeora la carga.

Documentación mínima que debe acompañar el modelo

ElementoQué documentarEjemplo
ÍndiceConsulta que lo justifica, columnas y orden, selectividad esperada, costo de escrituraIDX_pedido_cliente_fecha para Q-01 (cliente_id, fecha)
VistaPropósito, tablas base, filtros “oficiales”, consumidoresvw_pedidos_activos: excluye cancelados
Materialización/ResumenFuente, periodicidad de refresco, desfase tolerado, monitoreoventas_diarias: refresco cada 5 min, desfase 10 min
DenormalizaciónCampo/tabla redundante, regla de verdad (actual vs histórico), mecanismo de mantenimientopedido.total_importe mantenido por trigger en items

Ejercicio guiado: diseñar para 3 consultas críticas sin romper integridad

Escenario

Supón un módulo de pedidos con estas consultas:

  • Q-01: pedidos por cliente (últimos 90 días), ordenados por fecha desc, paginados.
  • Q-02: detalle de un pedido con sus ítems (por pedido_id).
  • Q-03: dashboard: total vendido por día (últimos 30 días).

Paso a paso

  1. Mapea filtros y ordenamientos:

    • Q-01 filtra por cliente_id y rango de fecha, ordena por fecha.
    • Q-02 accede por PK pedido_id y join por FK en ítems.
    • Q-03 agrega por fecha.
  2. Decide índices mínimos:

    • Índice compuesto para Q-01: (cliente_id, fecha).
    • Índice en FK de ítems para Q-02: pedido_item(pedido_id).
    • Para Q-03, si se calcula en vivo: índice por fecha en pedidos (o en tabla de pagos/ventas según el modelo).
  3. Evalúa materialización para Q-03:

    • Si Q-03 se consulta constantemente y la tabla crece rápido, crea una tabla resumen ventas_diarias (o vista materializada equivalente).
    • Define frescura: por ejemplo, desfase aceptable 5 minutos.
  4. Define mecanismo de mantenimiento:

    • Para ventas_diarias: job cada 5 minutos que recalcula solo el día actual y ajusta el anterior (por correcciones tardías).
    • Monitoreo: registrar última ejecución exitosa y filas afectadas.
  5. Valida integridad y reglas:

    • Los índices no cambian reglas, solo acceso.
    • La tabla resumen debe derivar de fuentes con integridad garantizada (por ejemplo, ventas confirmadas).
    • Documenta si el dashboard es eventual (y cuánto).
  6. Documenta y prueba:

    • Asocia cada índice/materialización a Q-01/Q-02/Q-03.
    • Prueba carga: mide latencia de lectura y el impacto en escrituras.

Ahora responde el ejercicio sobre el contenido:

Al diseñar un modelo orientado a consultas para un listado de pedidos por cliente filtrado por rango de fechas y ordenado por fecha descendente, ¿qué decisión de indexación suele alinearse mejor con la ruta de acceso esperada?

¡Tienes razón! Felicitaciones, ahora pasa a la página siguiente.

¡Tú error! Inténtalo de nuevo.

Un índice compuesto con cliente_id primero y fecha después suele apoyar el patrón de WHERE por cliente y rango de fechas, y facilita el ORDER BY fecha. Indexar columnas de baja selectividad o asumir que una vista almacena datos no resuelve la ruta de acceso.

Siguiente capítulo

Validación del Modelo de Datos con Casos Reales y Pruebas de Consistencia

Arrow Right Icon
Portada de libro electrónico gratuitaModelado de Datos desde Cero: Fundamentos para Bases de Datos Profesionales
89%

Modelado de Datos desde Cero: Fundamentos para Bases de Datos Profesionales

Nuevo curso

9 páginas

Descarga la aplicación para obtener una certificación gratuita y escuchar cursos en segundo plano, incluso con la pantalla apagada.