PostgreSQL - procesy



Jak sprawdzić procesy i blokady? Jakie zapytania się aktualnie wykonują i od jakiego czasu? Odpowiedzi na takie i inne pytania znajdziecie poniżej.


Procesy

Ze względu na różne wersje postgres'a i rózne wersje systemów operacyjnych, na których postgresql jest zainstalowany poniższe zapytania mogą różnić się nazewnictwem kolumn. Zwłaszcza tabela pg_stat_activity, w której są przechowywane informacje o procesach działających w bazie danych. Każdy wiersz w tabeli odpowiada jednemu procesowi w bazie danych. Tak więc jeżeli któreś z poniższych zapytań zwraca błąd to proponuję sprawdzić nazwy kolumn.
Poniższe zapytanie wyświetla wszystkie procesy zachodzące w bazie danych. Z wyniku zapytania wykluczyłem procesy IDLE, gdyż w większości przypadków są to procesy nie obciążające systemu, często czekające w kolejce na swoją kolej lub takie które z jakiejś przyczyny nie zostały zakończone. Jeżeli takie procesy występują pomimo braku aktywności na serwerze bazodanowym to powinniśmy zainteresować się tą sytuacją, gdyż to oznacza, że coś działa nie tak jak powinno.

SELECT pid, query_start, query
FROM pg_stat_activity
WHERE query != '<IDLE>'
ORDER BY query_start;

 pid  |          query_start          |              query
------+-------------------------------+---------------------------------
 1500 | 2016-04-14 20:39:05.360473+02 | SELECT pid, query_start, query +
      |                               | FROM pg_stat_activity          +
      |                               | WHERE query != '<IDLE>'        +
      |                               | ORDER BY query_start;
(1 wiersz)

Pięć najdłużej wykonujących się zapytań

SELECT now()-query_start AS running_for, query 
FROM pg_stat_activity 
ORDER BY 1 DESC 
LIMIT 5;

 running_for |                                             query
-------------+-----------------------------------------------------------------------------------------------
 00:00:00    | SELECT now()-query_start AS running_for, query FROM pg_stat_activity ORDER BY 1 desc limit 5;
(1 wiersz)

Postgres w połączeniu z linuxem daje nam bardzo duże możliwości tworzenia prostych narzędzi skryptowych do obsługi bazy. Takie narzędzia mogą powstać po stronie bazy w PL/pgSQL, a moga i być tworzone w shellu. Przykładem może być powyższe zapytanie, wrzucone do skryptu lub jako polecenie wykonywane z poziomu crontab'a. Daje nam to możliwość prowadzenia regularnych statystyk.

while psql -qt -c "SELECT now()-query_start AS running_for, query FROM pg_stat_activity" >> querry_stats.txt; do sleep 1; done

Sprawdzenie PID'a w bazie

SELECT query 
FROM pg_stat_activity 
WHERE pid = 1500;

                        query
------------------------------------------------------
 SELECT query FROM pg_stat_activity WHERE pid = 1500;
(1 wiersz)

Przerwanie zapytania - jeżeli mamy jakiś proces który musimy z jakiegos powodu przerwać. Tutaj w miejsce 1500 wstawiamy numer PID'a który chcemy przerwać.

SELECT pg_cancel_backend(1500);

Blokady na bazie

SELECT  datname, locktype, 
relation::regclass, mode, 
transactionid      AS tid,
virtualtransaction AS vtid, 
pid, granted
FROM pg_catalog.pg_locks l 
LEFT JOIN pg_catalog.pg_database db ON db.oid = l.database 
WHERE true 
--(db.datname = 'dbname' OR db.datname IS NULL)
AND NOT pid = pg_backend_pid()
ORDER BY 3 ASC;

W postgres'ie mamy także dostęp do tabeli pg_locks, która zapewnia informacje o lock'ach zakładanych przez transakcje wykonywane na serwerze bazodanowym. Informacje z tej tabeli możemy połaczyć z naszą tabelą trzymającą informacje o aktywności.

SELECT * FROM pg_locks pl 
LEFT JOIN pg_stat_activity psa ON pl.pid = psa.pid;

Ok, mamy te wszystkie tabele. A jak to wszystko wyświetlić w bardziej przyjazny sposób? Poniżej odpowiedź.

SELECT bl.pid  AS blocked_pid,
    a.usename  AS blocked_user,
    kl.pid     AS blocking_pid,
    ka.usename AS blocking_user,
    a.query    AS blocked_statement
FROM  pg_catalog.pg_locks bl
JOIN pg_catalog.pg_stat_activity a  ON a.pid = bl.pid
JOIN pg_catalog.pg_locks         kl ON kl.transactionid = bl.transactionid AND kl.pid != bl.pid
JOIN pg_catalog.pg_stat_activity ka ON ka.pid = kl.pid
WHERE NOT bl.granted;

 blocked_pid | blocked_user | blocking_pid | blocking_user | blocked_statement
-------------+--------------+--------------+---------------+-------------------
(0 wierszy)

Można oczywiście robić różnorodne wariacje powyższych zapytań.

SELECT bl.pid    AS blocked_pid,
       a.usename AS blocked_user,
       ka.query  AS blocking_statement,
       now() - ka.query_start AS blocking_duration,
       kl.pid                 AS blocking_pid,
       ka.usename             AS blocking_user,
       a.query                AS blocked_statement, 
       now() - a.query_start  AS blocked_duration
FROM  pg_catalog.pg_locks bl
JOIN pg_catalog.pg_stat_activity a  ON a.pid = bl.pid
JOIN pg_catalog.pg_locks         kl ON kl.transactionid = bl.transactionid AND kl.pid != bl.pid
JOIN pg_catalog.pg_stat_activity ka ON ka.pid = kl.pid;

PostgreSQL - sesje użytkowników