Monitoramento PostgreSQL
Sabemos que existem diferentes
formas de monitorar as atividades no SGBD PostgreSQL.
Recentemente, fui solicitado em
uma consultoria de PostgreSQL, para uma aplicação escrita em java utilizando
hibernate (com muuuiiiittttooooos HQLs), foi solicitado que verificasse os
possíveis pontos de gargalo no servidor de banco de dados.
Afinal, “o problema sempre (ou
quase) é no banco de dados” (Pensamento popular quando o banco de dados
“aparentemente” está lento).
Vamos lá!
Verifiquei os parâmetros de
configuração do postgresql.conf em conjunto com os parâmetros de configuração
do Sistema Operacional (Redhat 7).
“Excelentes” DBAs já haviam feito
um “excelente” trabalho de tuning e
comprovei que os parâmetros de configuração estavam ajustados para a melhor
performance de utilização do SGBD, pelo menos para aquela realidade.
Procedimentos
Investigando mais a fundo o
motivo, identificamos que algumas transações, oriundas da aplicação, estavam
dentro de transações que aguardavam muito tempo por uma ação do usuário, isso
quando não perdiam a sessão e a aplicação simplesmente não fechava a conexão.
Com isso, foi inevitável o aumento de requisições para novas conexões ativas no
SGBD.
Bingo! Problema na aplicação que
foi mal escrita.
Os procedimentos a seguir foram
realizados em um servidor Postgres 9.3.10, em Julho/2016, em dois dias de
monitoramento.
É claro que existem outras formas
de realizar esse monitoramento!!!
Ao consultar a view
pg_stat_activity, observei que haviam muitas requisições cujo estado era idle_in_transaction.
O que precisava ser feito?
Capturar essas queries e reportar para que a equipe de desenvolvimento pudesse
agir pontualmente e buscar uma solução definitiva para o problema.
Consulta utilizada para buscar atividades no SGBD com o estado idle_in_transaction:
|
SELECT pid, datname, usename, client_addr,
query_start, state_change, state, query
FROM pg_stat_activity
WHERE state = 'idle_in_transaction';
|
Criação de estrutura no banco de dados para coleta dos dados:
|
-bash-4.1$ psql
Password:
psql.bin (9.2.4)
Type "help" for help.
[?1034hpostgres=#
postgres=#
postgres=#
postgres=#
postgres=# CREATE ROLE usr_monitoramento LOGIN
ENCRYPTED PASSWORD 'md53fed9f02a9f95c16b62bf2f718ce74b0' VALID UNTIL
'infinity';
CREATE ROLE
postgres=#
postgres=#
postgres=#
postgres=# CREATE DATABASE bd_monitoramento WITH
ENCODING='UTF8' OWNER=usr_monitoramento CONNECTION LIMIT=-1;
CREATE DATABASE
postgres=#
postgres=#
postgres=#
postgres=# CREATE TABLE atividades (
postgres=# pid
integer,
postgres=#
datname name,
postgres=#
usename name,
postgres=#
client_addr inet,
postgres=#
query_start timestamp with time zone,
postgres=#
state_change timestamp with time zone,
postgres=#
state text,
postgres=#
query text
postgres=#);
CREATE TABLE
postgres=#
postgres=#
postgres=#
postgres=# ALTER TABLE atividades OWNER TO
usr_monitoramento;
ALTER TABLE
postgres=#
postgres=#
postgres=#
postgres=# CREATE TABLE bloqueio (
postgres=# hora_atual timestamp with time zone,
postgres=# pid_bloqueado
integer,
postgres=# hora_inicio_bloqueada timestamp with time
zone,
postgres=# hora_mudanca_estado_bloqueada timestamp
with time zone,
postgres=# usuario_bloqueado name,
postgres=# querie_bloqueada text,
postgres=# aplicacao_bloqueada text,
postgres=# pid_bloqueio integer,
postgres=# hora_inicio_bloqueio timestamp with time
zone,
postgres=# hora_mudanca_estado_bloqueio timestamp with
time zone,
postgres=# usuario_bloqueio name,
postgres=# querie_bloqueio text,
postgres=# aplicacao_bloqueio
text
postgres=#);
CREATE TABLE
postgres=#
postgres=#
postgres=#
postgres=# ALTER TABLE bloqueio OWNER TO usr_monitoramento;
ALTER TABLE
|
Com a consulta a seguir, pude extrair as queries mais frequentes com estado
idle_in_transaction:
|
SELECT pid, query, COUNT(0) qtde INTO
atividades_agrupadas FROM atividades GROUP BY pid, query ORDER BY 3 DESC;
|
Consulta para capturar as possíveis requisições que estão bloqueando e que
são bloqueadas:
|
SELECT
now() hora_atual,
atividade_bloqueada.pid pid_bloqueado,
atividade_bloqueada.query_start hora_incio_bloqueada,
atividade_bloqueada.state_change hora_mudanca_estado_bloqueada,
atividade_bloqueada.usename usuario_bloqueado,
atividade_bloqueada.query querie_bloqueada,
atividade_bloqueada.application_name
aplicacao_bloqueada,
bloqueio.pid pid_bloqueio,
atividade_bloqueio.query_start hora_incio_bloqueio,
atividade_bloqueio.state_change hora_mudanca_estado_bloqueio,
atividade_bloqueio.usename usuario_bloqueio,
atividade_bloqueio.query querie_bloqueio,
atividade_bloqueio.application_name
aplicacao_bloqueio
FROM
pg_catalog.pg_locks bloqueados
JOIN pg_catalog.pg_stat_activity
atividade_bloqueada ON
atividade_bloqueada.pid = bloqueados.pid
JOIN pg_catalog.pg_locks bloqueio
ON bloqueio.locktype = bloqueados.locktype
AND bloqueio.DATABASE IS NOT DISTINCT FROM
bloqueados.DATABASE
AND bloqueio.relation IS NOT DISTINCT FROM
bloqueados.relation
AND bloqueio.page IS NOT DISTINCT FROM
bloqueados.page
AND bloqueio.tuple IS NOT DISTINCT FROM
bloqueados.tuple
AND bloqueio.virtualxid IS NOT DISTINCT FROM
bloqueados.virtualxid
AND bloqueio.transactionid IS NOT DISTINCT FROM
bloqueados.transactionid
AND bloqueio.classid IS NOT DISTINCT FROM
bloqueados.classid
AND bloqueio.objid IS NOT DISTINCT FROM
bloqueados.objid
AND bloqueio.objsubid IS NOT DISTINCT FROM
bloqueados.objsubid
AND bloqueio.pid != bloqueados.pid
JOIN pg_catalog.pg_stat_activity atividade_bloqueio
ON atividade_bloqueio.pid = bloqueio.pid
WHERE NOT bloqueados.granted;
|
Criação do arquivo monitoramento.sh:
|
#!/bin/sh
# Coleta de dados PG_STAT_ACTIVITY e BLOQUEIO
# Data de criacao: 2016-07-01
# Criado por: Anderson Abreu
# E-mail: andersonabreu@gmail.com
export PGPASSWORD="edb2013"
INICIO="Inicio: "
FIM="Final: "
D=$(date '+%d/%m/%Y %R:%S')
echo $INICIO$D >> /tmp/log_atividades.txt
/opt/PostgreSQL/9.2/bin/psql -U postgres -w -d
bd_monitoramento -c "insert into atividades select pid, datname,
usename, client_addr, query_start, state_change, state, query from pg_stat_activity
where state = 'idle_in_transaction';" >> /tmp/log_atividades.txt
/opt/PostgreSQL/9.2/bin/psql -U postgres -w -d
bd_monitoramento -c "insert into bloqueio SELECT
now() hora_atual,
atividade_bloqueada.pid pid_bloqueado,
atividade_bloqueada.query_start hora_incio_bloqueada,
atividade_bloqueada.state_change
hora_mudanca_estado_bloqueada,
atividade_bloqueada.usename usuario_bloqueado,
atividade_bloqueada.query querie_bloqueada,
atividade_bloqueada.application_name
aplicacao_bloqueada,
bloqueio.pid pid_bloqueio,
atividade_bloqueio.query_start hora_incio_bloqueio,
atividade_bloqueio.state_change
hora_mudanca_estado_bloqueio,
atividade_bloqueio.usename usuario_bloqueio,
atividade_bloqueio.query querie_bloqueio,
atividade_bloqueio.application_name aplicacao_bloqueio
FROM
pg_catalog.pg_locks bloqueados
JOIN pg_catalog.pg_stat_activity
atividade_bloqueada ON
atividade_bloqueada.pid = bloqueados.pid
JOIN pg_catalog.pg_locks bloqueio
ON
bloqueio.locktype = bloqueados.locktype
AND
bloqueio.DATABASE IS NOT DISTINCT FROM bloqueados.DATABASE
AND
bloqueio.relation IS NOT DISTINCT FROM bloqueados.relation
AND
bloqueio.page IS NOT DISTINCT FROM bloqueados.page
AND
bloqueio.tuple IS NOT DISTINCT FROM bloqueados.tuple
AND bloqueio.virtualxid IS NOT DISTINCT
FROM bloqueados.virtualxid
AND
bloqueio.transactionid IS NOT DISTINCT FROM bloqueados.transactionid
AND
bloqueio.classid IS NOT DISTINCT FROM bloqueados.classid
AND
bloqueio.objid IS NOT DISTINCT FROM bloqueados.objid
AND
bloqueio.objsubid IS NOT DISTINCT FROM bloqueados.objsubid
AND
bloqueio.pid != bloqueados.pid
JOIN pg_catalog.pg_stat_activity atividade_bloqueio
ON atividade_bloqueio.pid = bloqueio.pid
WHERE NOT bloqueados.granted;" >>
/tmp/log_atividades.txt
D=$(date '+%d/%m/%Y %R:%S')
echo $FIM$D >> /tmp/log_atividades.txt
|
Agendamento da rotina:
Agendei a execução para ocorrer a
cada minuto para termos o máximo de dados e suas possíveis repetições.
|
# crontab -e
* * *
* * /tmp/monitoramento.sh
|
Report para equipe de desenvolvimento
Feita a coleta de dados, foi
realizada a exportação dos dados para um arquivo no formato CSV para que fosse
possível a análise da equipe de desenvolvimento.
|
copy atividades_agrupadas to '/tmp/atividades_agrupadas_idle_in_transaction.csv'
DELIMITER ';' CSV HEADER;
copy bloqueio to '/tmp/bloqueio.csv' DELIMITER ';'
CSV HEADER;
|

Nenhum comentário:
Postar um comentário