Olá Roberto,

> > Deparei-me com uma trigger em Postgres que para um determinado caso está
> > horrivelmente lenta. O cenário é o seguinte: temos uma tabela de
> > lançamentos, com uso pesado e simultâneo, que atualiza via trigger uma
> > tabela de saldos, usando trigger functions do tipo ON EACH ROW, que
determinam
> > a diferença de valores entre o OLD e o NEW para saber o que atualizar na
> > tabela de saldos.
> 
> Um dos motivos por que eu prefiro não usar tabelas cujos dados são
> resultados de cálculos de outras tabelas. Prefiro usar VIEWs.

Uma VIEW totalizando saldos sobre uma tabela de 130000 registros?!?!?!?!
Meu cliente não tem tempo ocioso para esperar servidor SQL nenhum ficar
somando essa tralha cada vez que ele resolver consultar o saldo de alguma
coisa. A consulta de saldos é muito mais frequente do que quaisquer
atualizações.
Minhas tabelas de saldos são 3, apenas com as chaves e valores (portanto
fisicamente muito menores). Elas tem 43000+11000+3700 registros, portanto
qualquer consulta sobre elas será sempre muito mais rápida do que qualquer
view sobre a tabela original.

> Não me parece tanto assim. Aqui vão algumas perguntas:
> 1) Quão complexos são esses cálculos?

Soma e subtração da diferença entre o valor antes e depois da alteração
em alguns registros das 3 tabelas de saldos. Cada registro individual alterado
na tabela de origem gera de 3 a 7 atualizações em cada tabela de saldos.
Quando maior o número de registros afetados na origem, maior a chance de
alterar várias vezes os mesmos registros nas tabelas de saldos.
Pouco posso fazer se não há maneira de identificar o que foi alterado a fim
do postgres atualizar cada registro uma vez só.

> 2) Quantas consultas em outras tabelas tem que ser feitas nesses cálculos?

Umas 3 consultas, uma simples e as outras 2 têm de 5 a 15 inner joins.
 
> 3) Já passaste essas consultas por seus devidos EXPLAIN para analisar
> como elas estão sendo planejadas?

As consultas individualmente são rápidas e têm todos os índices que
poderiam ajudar.
A proporção de registros trazida por cada código de conta varia pouco, o
plano de execução pode perfeitamente ser o mesmo para todas elas, tanto que
não há casos de baixo desempenho para atualizações em registros
individuais.
 
> 4) Como está a sua work_mem e parâmetros relacionados de memória? Como
> estão seus parâmetros de checkpoints? Será que o PG não está gastando
> muito tempo fazendo checkpoints, ou tendo pouca memória para
> trabalhar?

Padrão do Postgres 8.4, arquivo de configuração montado pelo assistente de
instalação.
A máquina de testes é um mísero Windows XP com 2 giga de RAM e um
processadorzinho de 1.5Ghz. A máquina do cliente é um servidor muito mais
potente, só que o problema de lentidão permanece e é reproduzido nas duas.

> 5) Como é a sua configuração de disco? Logs estão em discos separados?
> Tabelas mais usadas, como essa de saldo, estão em tablespaces
> separados?

Não, tudo está socado num só disco IDE com um só tablespace. O SQL Server
também.
Não consigo ver relevância na distribuição física dos arquivos ou na
quantidade de discos, ao menos não a ponto de algo que leva 10 horas cair
para 5 segundos por causa de um disco extra. Se dois discos fizessem o
desempenho dobrar (circunstância ideal inatingível), eu ainda teria algo
3600 vezes pior do que o esperado.

> Tem algo errado aí. Será que isso é um caso da aplicação estar sendo
> desenvolvida (e otimizada) no SQL Server e portada sem o mesmo cuidado
> para PG?

O que a aplicação faz é dar um DELETE que afeta 3000 registros. Rodando por
dentro ou por fora dela, o desempenho é o mesmo. Fiz os testes com uma única
conexão ao servidor, para excluir lentidão por acesso concorrente.
Quanto à trigger, ela faz em SQL Server exatamente as mesmas coisas que a sua
versão em Postgres. As consultas são exatamente as mesmas, as ordem de
atualização também. A única coisa que não consegui igualar foi a
existência das tabelas 'inserted' e 'deleted', que simulei movendo os dados
atualizados para tabelas auxiliares (no SQL Server também movi, só que para
tabelas temporárias). Dessa forma, apenas trocando os nomes mantive a mesma
lógica nos dois bancos de dados, com exatamente os mesmos joins sobre as
tabelas contendo os dados modificados pela transação.

> Se os cálculos são complexos e tem que ser feitos ANTES dos resultados
> serem enviados ao disco e transação COMMITted, como pode o SQL Server
> fazer em 5 segundos? 

Porque ele executa essa lógica toda uma única vez, com a tabela 'deleted'
contendo 3000 registros. No Postgres eu não tenho em nenhum lugar a lista de
registros modificados, e por isso a trigger do postgres precisa fazer o
processo 3000 vezes, cada uma delas com as tabelas auxiliares tendo um
registro. Sempre soubemos desde a primeira implementação que havia essa
diferença, simplesmente convivemos com ela até achar opção melhor. Quando
alguém tiver uma, aceitarei de bom grado.

> Se o SQL Server estiver apenas criando as tabelas
> "virtuais" e retornando controle ao usuário, para depois executar a
> checagem e escrita em disco usando as tabelas virtuais, então o
> comportamento está errado, de acordo com o que você disse que não pode
> acontecer abaixo (usando uma tabela separada)

Não, é que ele é rápido assim mesmo. Temos essas triggers rodando há anos
em diversos clientes nos dois bancos de dados e em todas as incontáveis
conferências que fizemos sempre encontramos os mesmos resultados (exceto que
um leva muito mais tempo que o outro).


Mozart Hasse


_______________________________________________
pgbr-geral mailing list
[email protected]
https://listas.postgresql.org.br/cgi-bin/mailman/listinfo/pgbr-geral

Responder a