Cómo hacer mantenimiento en PostgreSQL sin parar producción

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

Deja una respuesta

Tu dirección de correo electrónico no será publicada. Los campos obligatorios están marcados con *