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
