Cuando tienes un montón de datos (como mensajes sobre deadlines en los chats de la empresa), las consultas para seleccionar y procesar empiezan a ir más lentas. Aquí tienes las razones principales:
- Falta de índices. Cuando PostgreSQL tiene que escanear toda tu tabla para ejecutar una consulta (esto se llama "Seq Scan" — escaneo secuencial), la consulta puede tardar bastante más.
- Consultas SQL ineficientes. Si tus consultas no están optimizadas, incluso con índices puedes tener problemas en producción. ¿Por ejemplo, te olvidaste de usar condiciones clave en
WHERE? Prepárate para esperar un buen rato. - Grandes volúmenes de datos en una sola tabla. Por ejemplo, si intentas analizar ventas de todos los años a la vez, ni los índices te van a salvar.
Pero tranqui, tenemos dos formas probadas para lidiar con esto: Indexación y Particionado.
Usando índices para acelerar consultas
Aquí tienes un ejemplo sencillo de cómo crear un índice:
CREATE INDEX idx_sales_date ON sales(transaction_date);
- Aquí
idx_sales_datees el nombre del índice (puedes llamarlo como quieras, pero mejor que tenga sentido). ON sales(transaction_date)— indica para qué tabla y columna se crea el índice.
Este índice es especialmente útil si filtras mucho por el campo transaction_date.
Ejemplo de consulta que se beneficia de este índice:
SELECT *
FROM sales
WHERE transaction_date BETWEEN '2023-01-01' AND '2023-12-31';
Indexación de claves compuestas
Si tus consultas suelen usar varias columnas a la vez, como region y product_id, piensa en crear un índice compuesto:
CREATE INDEX idx_sales_region_product ON sales(region, product_id);
Ahora las consultas como esta van mucho más rápido:
SELECT *
FROM sales
WHERE region = 'North America' AND product_id = 42;
Usando índices únicos
Los índices únicos no solo aceleran la búsqueda, sino que también garantizan que los valores en la columna sean únicos. Por ejemplo:
CREATE UNIQUE INDEX idx_unique_customer_email ON customers(email);
Ahora no podrás meter dos clientes con el mismo correo por accidente.
Indexación para funciones analíticas
Algunas funciones de análisis de datos, como SUM, COUNT o AVG, pueden usar el índice para calcular más rápido. Mira este ejemplo:
CREATE INDEX idx_sales_amount ON sales(amount);
Consulta:
SELECT SUM(amount)
FROM sales
WHERE transaction_date >= '2023-01-01';
irá más rápido gracias al índice.
Particionado de tablas para trabajar con grandes volúmenes de datos
El particionado de tablas es el proceso de dividir una tabla grande en partes lógicas más pequeñas llamadas particiones. Por ejemplo, puedes dividir la tabla sales en particiones por año: sales_2021, sales_2022, etc.
¿Te parece complicado? En realidad, PostgreSQL lo hace más fácil de lo que parece.
Tipos de particionado
- Particionado por rango (
Range Partitioning). Los datos se dividen según un rango, por ejemplo, por fecha. - Particionado por lista (
List Partitioning). Los datos se dividen según valores exactos, por ejemplo, por regiones. - Particionado por hash (
Hash Partitioning). Usa una función hash para dividir los datos (se usa menos a mano).
Creando una tabla particionada
Vamos a crear una tabla de ventas particionada por año.
CREATE TABLE sales (
id SERIAL PRIMARY KEY,
transaction_date DATE NOT NULL,
amount NUMERIC,
region TEXT
) PARTITION BY RANGE (transaction_date);
Ahora creamos particiones para diferentes años:
CREATE TABLE sales_2021 PARTITION OF sales
FOR VALUES FROM ('2021-01-01') TO ('2022-01-01');
CREATE TABLE sales_2022 PARTITION OF sales
FOR VALUES FROM ('2022-01-01') TO ('2023-01-01');
Las consultas que filtran por fecha automáticamente solo trabajan con la partición necesaria. Puedes comprobarlo fácilmente con el comando EXPLAIN.
Ejemplo con particionado
Así sería una consulta para sumar ventas solo de 2021:
SELECT SUM(amount)
FROM sales
WHERE transaction_date BETWEEN '2021-01-01' AND '2021-12-31';
Como ves, PostgreSQL solo trabaja con la partición sales_2021 necesaria, y no escanea toda la tabla.
Ejemplo: optimización del cálculo de métricas por regiones
Supón que quieres calcular la suma total de ventas por regiones. Sin índices ni particiones, esto tarda una eternidad. Primero creamos un índice para la columna region:
CREATE INDEX idx_sales_region ON sales(region);
Tu consulta:
SELECT region, SUM(amount)
FROM sales
GROUP BY region;
Ahora el procesamiento va más rápido gracias al índice.
Ejemplo: particionado de datos temporales
Para datos temporales, como transacciones o logs, crea particiones por meses. Por ejemplo:
CREATE TABLE sales_monthly PARTITION BY RANGE (transaction_date);
CREATE TABLE sales_jan_2023 PARTITION OF sales_monthly
FOR VALUES FROM ('2023-01-01') TO ('2023-02-01');
Consulta:
SELECT SUM(amount)
FROM sales_monthly
WHERE transaction_date >= '2023-01-01' AND transaction_date < '2023-02-01';
irá más rápido porque PostgreSQL solo lee la partición sales_jan_2023.
Ejemplo: combinando indexación y particionado
Puedes combinar indexación y particionado para lograr el máximo rendimiento. Por ejemplo, puedes crear índices dentro de cada partición. Mira este ejemplo:
CREATE INDEX idx_sales_amount_jan_2023 ON sales_jan_2023(amount);
Cómo evitar errores típicos
Muchos problemas de rendimiento vienen de usar mal los índices y el particionado. Por ejemplo:
- Tener demasiados índices puede hacer más lentas las operaciones de inserción.
- Las particiones deben estar equilibradas; particiones demasiado pequeñas o demasiado grandes bajan el rendimiento.
- Olvidar analizar el rendimiento (
EXPLAIN ANALYZE) antes de optimizar — es como intentar arreglar el coche sin mirar debajo del capó.
Siempre revisa si tus optimizaciones realmente mejoran la velocidad, y no tengas miedo de experimentar.
GO TO FULL VERSION