sqlpostgresqlmaterialized-viewperformance

Vistas materializadas en SQL: cachear consultas costosas

Como una vista materializada cachea el resultado de una consulta pesada, como funciona REFRESH y cuando conviene una vista normal o una tabla resumen.

2 min de lecturaReferencesql · postgresql · materialized-view · performance · caching

Una vista materializada es una consulta cuyo resultado se almacena fisicamente en disco como una tabla. Pagas el SELECT pesado una sola vez, al refrescar, y luego lees la respuesta precalculada en milisegundos.

Para que sirve

Una VIEW normal es solo texto de consulta guardado: cada acceso vuelve a ejecutar el plan. Si agrega millones de filas o une media tabla, tu panel se arrastra. Una vista materializada calcula el resultado por adelantado y lo conserva.

CREATE MATERIALIZED VIEW revenue_by_country AS
SELECT u.country,
       count(*)        AS orders_cnt,
       sum(o.amount)   AS revenue
FROM orders o
JOIN users u ON u.id = o.user_id
WHERE o.status = 'paid'
GROUP BY u.country;

Ahora SELECT * FROM revenue_by_country lee filas ya calculadas sin tocar orders ni users. La pega: los datos quedan congelados en el momento del ultimo refresco.

Refrescar: REFRESH y CONCURRENTLY

Para traer datos frescos, recalculas la vista:

REFRESH MATERIALIZED VIEW revenue_by_country;

El problema: un REFRESH normal toma un bloqueo ACCESS EXCLUSIVE, asi que los lectores se bloquean mientras corre. En conjuntos grandes son segundos de parada. La solucion es CONCURRENTLY: los nuevos datos se construyen en segundo plano y se intercambian de forma atomica sin bloquear SELECT.

-- CONCURRENTLY necesita un indice UNIQUE sobre las filas del resultado
CREATE UNIQUE INDEX ON revenue_by_country (country);

REFRESH MATERIALIZED VIEW CONCURRENTLY revenue_by_country;
  • Sin indice unico, CONCURRENTLY lanza un error.
  • CONCURRENTLY es mas lento (compara fila por fila) pero nunca bloquea las lecturas.
  • Lanza el refresco con un planificador (cron, pg_cron) o como paso posterior al ETL.

Indexar la vista

Una vista materializada es una tabla, asi que puedes ponerle indices normales y acelerar las lecturas sobre ella.

CREATE INDEX idx_rev_revenue ON revenue_by_country (revenue DESC);

SELECT country, revenue
FROM revenue_by_country
ORDER BY revenue DESC
LIMIT 10;

Los indices sobreviven a un REFRESH: Postgres los reconstruye por ti. Eso si: cuantos mas indices anadas, mas lento sera cada refresco.

Datos obsoletos: el compromiso clave

El precio de la velocidad son datos obsoletos. Entre dos REFRESH, la vista muestra una foto antigua.

Gotcha: un usuario hace un pedido, pero no aparece en revenue_by_country hasta que llamas a REFRESH. Nunca uses una vista materializada donde necesites exactitud transaccional (saldos de cuenta, stock).

Ajusta la frecuencia al negocio: un panel analitico va bien refrescando cada hora; el casi tiempo real necesita cada par de minutos.

View, MV o tabla resumen

Opcion Datos Cuando usar
VIEW siempre frescos consulta ligera, se exige actualidad
MATERIALIZED VIEW del ultimo refresco consulta pesada, tolera retraso
tabla resumen la mantienes tu necesitas logica incremental y UPSERT

Una tabla resumen es el equivalente manual: la rellenas con INSERT ... SELECT y la actualizas de forma incremental tu mismo.

INSERT INTO revenue_summary (country, revenue, day)
SELECT u.country, sum(o.amount), current_date
FROM orders o
JOIN users u ON u.id = o.user_id
WHERE o.created_at >= current_date
GROUP BY u.country
ON CONFLICT (country, day) DO UPDATE
SET revenue = EXCLUDED.revenue;

Diferencias por motor:

  • MySQL no tiene vistas materializadas en absoluto: las emulas con tablas resumen mas triggers o el planificador de eventos.
  • ClickHouse tiene MATERIALIZED VIEW, pero no es un cache: es un trigger incremental que actualiza agregados en cada insercion en la tabla origen.

La regla es simple: necesitas actualidad, usa una VIEW; la consulta es cara y el retraso es aceptable, usa una MATERIALIZED VIEW; necesitas logica incremental fina, monta tu propia tabla resumen.

Practica con ejercicios reales

Resuelve ejercicios en el entrenador de SQL con corrección instantánea y pistas.

Abrir el entrenador