Bloqueos en PostgreSQL

Hola a todos, hoy trataremos como los bloqueos en PostgreSQL, causados por la concurrencia de múltiples conexiones, pueden afectar significativamente al rendimiento de la base de datos.

Identificar, diagnosticar y resolver estos bloqueos es esencial para mantener una operación eficiente, exploraremos técnicas avanzadas para visualizar y diagnosticar bloqueos, así como estrategias para optimizar la base de datos y evitar estos problemas. También profundizaremos en cómo registrar bloqueos a lo largo del tiempo para analizar patrones y mejorar el rendimiento de manera proactiva.

Introducción a los Bloqueos en PostgreSQL

Los bloqueos en PostgreSQL son mecanismos esenciales para proteger la integridad y consistencia de los datos durante operaciones concurrentes. Estos bloqueos pueden ocurrir a varios niveles, desde filas individuales hasta tablas completas. Los tipos más comunes de bloqueos incluyen:

  • Bloqueos a Nivel de Fila (Row-Level Locks): Utilizados para proteger filas individuales durante operaciones como actualizaciones y eliminaciones, permitiendo que múltiples transacciones modifiquen diferentes filas de la misma tabla sin interferencias.
  • Bloqueos a Nivel de Tabla (Table-Level Locks): Utilizados para operaciones que afectan a toda la tabla, como cambios en la estructura de la tabla (ALTER TABLE).

Visualización de Bloqueos en PostgreSQL

Uso de pg_locks

La vista pg_locks es una herramienta fundamental para monitorear y diagnosticar bloqueos en PostgreSQL. Proporciona información detallada sobre todos los bloqueos actuales en la base de datos.

SELECT pid, locktype, relation::regclass AS table, page, tuple, virtualxid, transactionid, classid, objid, virtualtransaction, mode, granted FROM pg_locks;

Esta consulta nos proporciona una instantánea de todos los bloqueos activos, mostrando el ID del proceso que sostiene el bloqueo (pid), el tipo de bloqueo (locktype), la tabla afectada (relation) y el estado del bloqueo (granted).

Identificación de Procesos Bloqueados y Bloqueantes

La combinación de pg_stat_activity con pg_locks nos permitirá identificar qué procesos están bloqueados y cuáles son los responsables de esos bloqueos.

SELECT blocked_locks.pid AS blocked_pid, 
blocked_activity.usename AS blocked_user,
blocking_locks.pid AS blocking_pid,
blocking_activity.usename AS blocking_user,
blocked_activity.query AS blocked_query,
blocking_activity.query AS blocking_query
FROM pg_locks blocked_locks
JOIN pg_stat_activity blocked_activity ON blocked_locks.pid = blocked_activity.pid
JOIN pg_locks blocking_locks ON blocked_locks.locktype = blocking_locks.locktype
AND blocked_locks.database IS NOT DISTINCT FROM blocking_locks.database
AND blocked_locks.relation IS NOT DISTINCT FROM blocking_locks.relation
AND blocked_locks.page IS NOT DISTINCT FROM blocking_locks.page
AND blocked_locks.tuple IS NOT DISTINCT FROM blocking_locks.tuple
AND blocked_locks.virtualxid IS NOT DISTINCT FROM blocking_locks.virtualxid
AND blocked_locks.transactionid IS NOT DISTINCT FROM blocking_locks.transactionid
AND blocked_locks.classid IS NOT DISTINCT FROM blocking_locks.classid
AND blocked_locks.objid IS NOT DISTINCT FROM blocking_locks.objid
AND blocked_locks.objsubid IS NOT DISTINCT FROM blocking_locks.objsubid
AND blocked_locks.pid <> blocking_locks.pid
JOIN pg_stat_activity blocking_activity ON blocking_locks.pid = blocking_activity.pid
WHERE NOT blocked_locks.granted;

Esta consulta es especialmente útil para identificar y resolver conflictos de bloqueo, permitiendo tomar acciones correctivas rápidamente.

bloqueos en postgresql

Registro de Bloqueos a lo Largo del Tiempo

El problema que tenemos con estas técnicas es que solo nos servirán para abordar los bloqueos que estén sucediendo en la base de datos en ese mismo momento, ya que no se almacena ningún tipo de histórico de los mismos. Por tanto, registrar bloqueos a lo largo del tiempo es crucial para analizar patrones y mejorar el rendimiento de manera proactiva. Una manera efectiva de hacerlo es mediante la captura periódica del estado de esas vistas (pg_stat_activity y pg_locks).

Creación de una Tabla de Registro

Primero, necesitamos una tabla para almacenar los registros de los bloqueos.

CREATE TABLE lock_log (
log_time TIMESTAMP DEFAULT current_timestamp,
blocked_pid INT,
blocked_user TEXT,
blocking_pid INT,
blocking_user TEXT,
blocked_query TEXT,
blocking_query TEXT,
locktype TEXT,
relation TEXT,
mode TEXT,
granted BOOLEAN
);

Captura de Datos de Bloqueo

Podemos usar un script o una tarea programada (cron job) para capturar el estado de los bloqueos periódicamente y almacenarlo en la tabla lock_log.

Ejemplo de script para registrar Bloqueos en PostgreSQL:

DO $$
BEGIN
LOOP
INSERT INTO lock_log (blocked_pid, blocked_user, blocking_pid, blocking_user, blocked_query, blocking_query, locktype, relation, mode, granted)
SELECT blocked_locks.pid AS blocked_pid,
blocked_activity.usename AS blocked_user,
blocking_locks.pid AS blocking_pid,
blocking_activity.usename AS blocking_user,
blocked_activity.query AS blocked_query,
blocking_activity.query AS blocking_query,
blocked_locks.locktype,
blocked_locks.relation::regclass AS relation,
blocked_locks.mode,
blocked_locks.granted
FROM pg_locks blocked_locks
JOIN pg_stat_activity blocked_activity ON blocked_locks.pid = blocked_activity.pid
JOIN pg_locks blocking_locks ON blocked_locks.locktype = blocking_locks.locktype
AND blocked_locks.database IS NOT DISTINCT FROM blocking_locks.database
AND blocked_locks.relation IS NOT DISTINCT FROM blocking_locks.relation
AND blocked_locks.page IS NOT DISTINCT FROM blocking_locks.page
AND blocked_locks.tuple IS NOT DISTINCT FROM blocking_locks.tuple
AND blocked_locks.virtualxid IS NOT DISTINCT FROM blocking_locks.virtualxid
AND blocked_locks.transactionid IS NOT DISTINCT FROM blocking_locks.transactionid
AND blocked_locks.classid IS NOT DISTINCT FROM blocking_locks.classid
AND blocked_locks.objid IS NOT DISTINCT FROM blocking_locks.objid
AND blocked_locks.objsubid IS NOT DISTINCT FROM blocking_locks.objsubid
AND blocked_locks.pid <> blocking_locks.pid
JOIN pg_stat_activity blocking_activity ON blocking_locks.pid = blocking_activity.pid
WHERE NOT blocked_locks.granted;
PERFORM pg_sleep(60);
END LOOP;
END $$;

Este script captura el estado de los bloqueos cada minuto y los guarda en la tabla lock_log.

Análisis de Datos de Bloqueo

Una vez registrados los datos, podemos analizarlos para identificar patrones y problemas recurrentes:

SELECT log_time, blocked_pid, blocked_user, blocking_pid, blocking_user, blocked_query, blocking_query, locktype, relation, mode, granted
FROM lock_log
WHERE log_time > now() - interval '1 day'
ORDER BY log_time DESC;

Ejemplo de Datos en la Tabla lock_log:

log_timeblocked_pidblocked_userblocking_pidblocking_userblocked_queryblocking_querylocktyperelationmodegranted
2024-07-01 12:00:002345user13456user2UPDATE orders SET status = ‘shipped’…DELETE FROM orders WHERE status = ‘shipped’relationordersACCESS EXCLUSIVEfalse
2024-07-01 12:01:004567user35678user4SELECT * FROM products WHERE price > …INSERT INTO products VALUES (1, ‘item’)tupleproductsROW EXCLUSIVEfalse

Agrupación de Bloqueos Recurrentes

Para identificar bloqueos recurrentes causados por la misma consulta o consultas similares, una opción sería agrupar los datos por la consulta bloqueada y la consulta bloqueante, aunque esto tiene infinidad de combinaciones y personalización:

SELECT blocked_query, blocking_query, COUNT(*) AS frequency
FROM lock_log
GROUP BY blocked_query, blocking_query
ORDER BY frequency DESC;

Esta consulta nos muestra las combinaciones de consultas bloqueadas y bloqueantes que ocurren con mayor frecuencia, ayudando a identificar patrones recurrentes y áreas que requieren optimización.

Ventajas del Registro de Bloqueos:

  • Permite rastrear y analizar bloqueos a lo largo del tiempo.
  • Facilita la identificación de patrones de bloqueo y posibles optimizaciones.
  • Proporciona una visión histórica que ayuda a resolver problemas recurrentes.

Desventajas:

  • Puede generar una gran cantidad de datos, requiriendo almacenamiento y gestión.
  • Requiere configuración de scripts y tareas programadas.

Análisis y Optimización de Bloqueos en PostgreSQL

Una vez que hemos identificado un bloqueo o hemos encontrado problemas recurrentes de bloqueos por una misma consulta, podemos utilizar diferentes estrategias para abordar el problema:

Análisis de Planes de Consulta: Utilizar EXPLAIN ANALYZE nos permitirá analizar cómo PostgreSQL planea y ejecuta una consulta, proporcionando información sobre tiempos de ejecución y uso de recursos, los cuales podrían ser un motivo de bloqueo.

Uso de pg_stat_statements: La extensión pg_stat_statements ayudará a rastrear el rendimiento de las consultas a lo largo del tiempo, identificando las más costosas y permitiendo su optimización.

Optimización de Índices: Crear y mantener índices adecuados mejorará significativamente el rendimiento de las consultas y reduce los bloqueos.

Particionamiento de Tablas: El particionamiento divide una tabla grande en tablas más pequeñas, mejorando el rendimiento y reduciendo los bloqueos.

Configuración de Parámetros de Bloqueo: Ajustar parámetros como lock_timeout y deadlock_timeout ayudará a manejar mejor los bloqueos.

Conclusión

Esperamos que os haya resultado interesante y os pueda servir de ayuda. La gestión de bloqueos en PostgreSQL es crucial para mantener el rendimiento y la eficiencia de la base de datos.

Utilizando técnicas avanzadas de visualización y diagnóstico, y aplicando estrategias de optimización, los DBAs podemos minimizar los problemas de bloqueo y asegurar un funcionamiento suave y rápido de la base de datos. Además, registrar los bloqueos a lo largo del tiempo nos proporcionará una visión histórica que ayuda a identificar y resolver problemas recurrentes.

Sentiros libres de comentar y debatir cualquier otra posible solución que veáis a este problema tan común que suele haber en todas las bases de datos 😊

No dudéis en poneros en contacto con nosotros si necesitáis ayuda o tenéis cualquier duda con la gestión de vuestras bases de datos.

Échale un vistazo a nuestros servicios 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 *