Cuando cinco minutos de mantenimiento pueden convertirse en una incidencia de producción. Zero-Downtime no significa Zero-Lock.
“¿Puedes crear ese índice sin parar producción?”
Son las 10:30 de la mañana.
El teléfono del DBA suena con una petición que, a primera vista, parece sencilla:
“Necesitamos crear un índice en la tabla ventas. La consulta está tardando demasiado.”
Nada especialmente extraño. Es una tarea habitual para cualquier administrador de bases de datos.
La primera reacción podría ser conectarse a PostgreSQL y ejecutar:
CREATE INDEX idx_ventas_fecha
ON ventas(fecha);
Problema resuelto.
¿O no?
La tabla tiene varios millones de registros, la aplicación está atendiendo usuarios y, por supuesto, no existe una ventana de mantenimiento disponible.
Entonces aparece la pregunta que realmente importa:
¿Cómo mejoramos el rendimiento sin convertir una tarea rutinaria de mantenimiento en una incidencia de producción?
Aquí empieza el verdadero trabajo de un DBA PostgreSQL.
1. Antes de tocar nada
El DBA no debe ejecutar inmediatamente el CREATE INDEX.
Primero debe saber:
- ¿Qué tamaño tiene la tabla?
- ¿Cuántas filas tiene?
- ¿Qué consultas están provocando el problema?
- ¿Existe ya algún índice parecido?
- ¿Hay actividad sobre la tabla?
- ¿Hay transacciones largas?
- ¿Cuánto espacio libre tenemos?
- ¿Qué impacto puede tener la operación?
Entonces empieza la investigación:
SELECT
count(*)
FROM ventas;
Después:
SELECT pg_size_pretty(pg_table_size('ventas')) AS tabla,
pg_size_pretty(pg_indexes_size('ventas')) AS indices,
pg_size_pretty(pg_total_relation_size('ventas')) AS total;
Y finalmente:
EXPLAIN (ANALYZE, BUFFERS)
SELECT
id,
cliente_id,
fecha,
importe
FROM ventas
WHERE fecha >= CURRENT_DATE - INTERVAL '30 days'
ORDER BY fecha DESC;
Por ejemplo:
Seq Scan on ventas
Execution Time: 4287 ms
Con esta información ya tenemos una primera conclusión:
“Tenemos un problema de acceso. Antes de modificar nada necesito saber si un índice realmente va a resolverlo.”
2. El DBA mira qué está pasando en producción
SELECT
pid,
usename,
application_name,
client_addr,
state,
wait_event_type,
wait_event,
query_start,
query
FROM pg_stat_activity
WHERE datname = current_database()
ORDER BY query_start;
Y encuentra algo como:
PID STATE QUERY START QUERY
---
12451 active 10:21:34 SELECT ...
12473 active 10:22:01 UPDATE ...
12502 idle 10:22:14
12518 active 10:25:47 SELECT ...
No basta con saber qué consulta queremos optimizar. Tenemos que conocer qué está ocurriendo alrededor de ella.
3. Aparece la primera decisión
Tenemos dos posibilidades.
Opción 1
CREATE INDEX idx_ventas_fecha
ON ventas(fecha);
Opción 2
CREATE INDEX CONCURRENTLY idx_ventas_fecha
ON ventas(fecha);
Y aquí aparece el verdadero dilema:
¿Cuál utilizarías en producción?
4. Y hacemos el experimento
Aquí es donde entra nuestro laboratorio.
Vamos a ejecutar ambas alternativas en condiciones controladas.
“Vamos a comprobarlo.”
Y procedemos a medir:
- Tiempo
- Locks
- CPU
- I/O
- Consultas concurrentes
- Impacto sobre INSERT/UPDATE
- Estado del índice
Podemos incluso monitorizar la creación del índice con:
SELECT
pid,
datname,
relid::regclass AS tabla,
index_relid::regclass AS indice,
phase,
lockers_total,
lockers_done,
current_locker_pid,
blocks_total,
blocks_done,
tuples_total,
tuples_done
FROM pg_stat_progress_create_index;
5. Entonces llega el momento crítico
Podemos hacer que nuestro laboratorio simule una transacción larga:
BEGIN;
UPDATE ventas
SET importe = importe + 0.01
WHERE id = 100000;
— Dejamos la transacción abierta
Y desde otra sesión:
SELECT pid,
locktype,
mode,
granted,
relation::regclass AS objeto
FROM pg_locks
WHERE relation IS NOT NULL
ORDER BY relation::regclass::text;
Ahora tenemos una situación realista:
Una transacción abierta + actividad de aplicación + mantenimiento del DBA.
CREATE INDEX CONCURRENTLY no significa que PostgreSQL deje de utilizar locks.
Significa que la operación está diseñada para permitir concurrencia y minimizar la interrupción del tráfico normal.
6. Y luego aparece otro ticket
15:40.
La incidencia inicial está resuelta.
La consulta que tardaba varios segundos ahora responde en unos cientos de milisegundos.
Parece que el día del DBA va a terminar tranquilo.
Entonces llega otro mensaje:
“El índice está creciendo demasiado. ¿Podemos reconstruirlo?”
Y volvemos a empezar.
Ahora aparece:
REINDEX INDEX idx_ventas_fecha;
Pero estamos en producción.
Entonces nos preguntamos:
REINDEX INDEX CONCURRENTLY idx_ventas_fecha;
Y volvemos a analizar:
- bloqueo;
- duración;
- espacio;
- impacto;
- monitorización.
7. Y todavía nos queda VACUUM
Al día siguiente:
“La tabla ha crecido muchísimo y queremos recuperar espacio.”
Y entonces aparece el clásico:
VACUUM FULL ventas;
El DBA se detiene.
Porque sabe que VACUUM FULL no es simplemente una versión más potente de VACUUM.
Entonces revisa:
VACUUM (VERBOSE, ANALYZE) ventas;
y estudia si necesita una alternativa de reorganización online.
En PostgreSQL, el trabajo de un DBA no consiste únicamente en saber qué comando ejecutar. Consiste en saber cuándo ejecutarlo, cómo ejecutarlo, qué impacto tendrá y cómo demostrar que la operación no está afectando al servicio.
La verdadera lección
Después de varias pruebas, la conclusión no es simplemente que CREATE INDEX CONCURRENTLY sea mejor que CREATE INDEX.
La verdadera lección es otra.
En producción, rara vez tenemos el lujo de trabajar sobre una base de datos detenida, sin usuarios y sin presión.
Las tablas están creciendo, las aplicaciones están ejecutando consultas, existen transacciones abiertas y siempre hay alguien esperando que el servicio continúe funcionando.
Por eso, una operación de mantenimiento no debería evaluarse únicamente preguntando:
“¿Funciona?”
La pregunta correcta es:
“¿Puedo hacerlo mientras el negocio sigue funcionando?”
Y ahí es donde PostgreSQL ofrece herramientas como CREATE INDEX CONCURRENTLY, REINDEX CONCURRENTLY, VACUUM y diferentes mecanismos de monitorización.
Pero incluso estas herramientas requieren criterio.
Porque Zero-Downtime no significa Zero-Lock.
Significa conocer los locks, anticiparlos, monitorizarlos y diseñar el mantenimiento para que el impacto sobre el servicio sea compatible con las necesidades del negocio.
Ese es, probablemente, uno de los aspectos menos visibles y más importantes del trabajo diario de un DBA.
Realizar cambios y tareas de mantenimiento en entornos productivos requiere algo más que conocer los comandos adecuados. Es necesario analizar el entorno, anticipar bloqueos y evaluar el impacto antes de actuar.
Si necesitas apoyo, estamos para ayudarte. Echale un ojo a nuestro Servicio de Soporte y Mantenimiento PostgreSQL.
¿Aún no conoces Query Performance? Descubre cómo puede ayudarte en tu entorno Oracle. Más información en su página de LinkedIn.
Sígue a GPS en LinkedIn
