Função Desempenho

46

Vindo de um histórico do MySQL, onde o desempenho do procedimento armazenado (artigo antigo) e a usabilidade são questionáveis, estou avaliando o PostgreSQL para um novo produto para minha empresa.

Uma das coisas que eu gostaria de fazer é mover parte da lógica do aplicativo para os procedimentos armazenados, portanto, estou aqui pedindo DOs e NÃO (melhores práticas) sobre o uso de funções no PostgreSQL (9.0), especificamente sobre problemas de desempenho.

Derek Downey
fonte
você quer dizer que as respostas não mencionem nada não relacionado ao desempenho?
Jack Douglas
Chris Travers bloga muito sobre as vantagens de usar procedimentos armazenados, por exemplo, aqui: ledgersmbdev.blogspot.de/2012/07/… e aqui: ledgersmbdev.blogspot.de/2012/07/… apenas percorra seu blog, há um muitos artigos interessantes sobre esse tópico.
a_horse_with_no_name

Respostas:

51

Estritamente falando, o termo "procedimentos armazenados" aponta para procedimentos SQL no Postgres, introduzidos no Postgres 11.

Também existem funções , fazendo quase, mas não exatamente o mesmo, e essas existem desde o início.

Funções com LANGUAGE sqlsão basicamente apenas arquivos em lote com comandos SQL simples em um wrapper de função (e, portanto, atômico, sempre executado dentro de uma única transação), aceitando parâmetros. Todas as instruções em uma função SQL são planejadas de uma vez , o que é sutilmente diferente de executar uma instrução após a outra e pode afetar a ordem na qual os bloqueios são executados.

Para qualquer coisa mais, a linguagem mais madura é PL / pgSQL ( LANGUAGE plpgsql). Funciona bem e foi aprimorado a cada versão na última década, mas serve melhor como cola para comandos SQL. Não se destina a cálculos pesados ​​(exceto com comandos SQL).

As funções PL / pgSQL executam consultas como instruções preparadas . A reutilização de planos de consulta em cache reduz algumas sobrecargas de planejamento e as torna um pouco mais rápidas que as instruções SQL equivalentes, o que pode ser um efeito perceptível dependendo das circunstâncias. Também pode ter efeitos colaterais, como nesta pergunta relacionada:

Isso traz as vantagens e desvantagens das declarações preparadas - conforme discutido no manual . Para consultas sobre tabelas com distribuição de dados irregular e parâmetros variáveis SQL dinâmica com EXECUTEpode ter um melhor desempenho quando o ganho de um plano de execução otimizado para o parâmetro dado (s) supera o custo de re-planejamento.

Desde o Postgres 9.2, os planos de execução genéricos ainda são armazenados em cache para a sessão, mas, citando o manual :

Isso ocorre imediatamente para instruções preparadas sem parâmetros; caso contrário, ocorrerá somente após cinco ou mais execuções produzirem planos cuja média de custo estimada (incluindo despesas gerais de planejamento) seja mais cara que a estimativa genérica de custo planejado.

Nós obtemos o melhor dos dois mundos na maioria das vezes (menos alguns custos adicionais) sem (ab) usar EXECUTE. Detalhes em O que há de novo no PostgreSQL 9.2 do PostgreSQL Wiki .

O Postgres 12 apresenta a variável de servidorplan_cache_mode adicional para forçar planos genéricos ou personalizados. Para casos especiais, use com cuidado.

Você pode ganhar muito com as funções do lado do servidor que impedem viagens adicionais de ida e volta ao servidor de banco de dados do seu aplicativo. Faça com que o servidor execute o máximo possível de uma só vez e retorne apenas um resultado bem definido.

Evite aninhar funções complexas, especialmente funções de tabela ( RETURNING SETOF recordou TABLE (...)). Funções são caixas pretas que se apresentam como barreiras de otimização para o planejador de consultas. Eles são otimizados separadamente, não no contexto da consulta externa, o que simplifica o planejamento, mas pode resultar em planos menos que perfeitos. Além disso, o custo e o tamanho do resultado das funções não podem ser previstos com confiabilidade.

A exceção a esta regra são funções SQL simples ( LANGUAGE sql), que podem ser "incorporadas" - se algumas pré-condições forem atendidas . Leia mais sobre como o planejador de consultas funciona nesta apresentação por Neil Conway (material avançado).

No PostgreSQL, uma função sempre é executada automaticamente dentro de uma única transação . Tudo isso dá certo ou nada. Se ocorrer uma exceção, tudo será revertido. Mas há tratamento de erros ...

É também por isso que as funções não são exatamente "procedimentos armazenados" (mesmo que esse termo seja usado algumas vezes de maneira enganosa). Alguns comandos gostam VACUUM, CREATE INDEX CONCURRENTLYou CREATE DATABASEnão podem ser executados dentro de um bloco de transação, portanto, não são permitidos em funções. (Ainda não nos procedimentos SQL, a partir do Postgres 11. Isso pode ser adicionado posteriormente.)

Eu escrevi milhares de funções plpgsql ao longo dos anos.

Erwin Brandstetter
fonte
2
@nhahtdh: "transação automática" não é um termo técnico. Era apenas uma maneira pouco elegante de dizer ... o que está dizendo agora depois do meu esclarecimento. Não é uma transação autônoma. "autônomo" passa a ser uma palavra semelhante.
Erwin Brandstetter
4
Suas respostas compiladas a partir daqui e SO podem ser um manual épico de boas práticas do PostGreSQL.
Davos
10

Alguns DO's:

  • Use SQL como a linguagem da função quando possível, pois o PG pode incorporar as instruções
  • Use IMMUTABLE / STABLE / VOLATILE corretamente, pois o PG pode armazenar em cache os resultados se for imutável ou estável
  • Use STRICT corretamente, pois o PG pode retornar nulo se alguma entrada for nula em vez de executar a função
  • Considere o PL / V8 quando não puder usar o SQL como a linguagem da função. É mais rápido que o PL / pgSQL em alguns testes não científicos que executei
  • Use LISTEN / NOTIFY para processos de execução mais longa que podem ocorrer fora da transação
  • Considere usar funções para implementar a paginação, pois a paginação com base em chave pode ser mais rápida que a paginação com LIMIT
  • Certifique-se de testar suas funções
Neil McGuigan
fonte
É a primeira vez que vejo a afirmação de que o PL / V8 é mais rápido que o PL / pgSQL. Você tem algum número (publicado) para apoiar isso?
a_horse_with_no_name
@a_horse_with_no_name não, eu não. Como eu disse, fiz alguns testes não científicos. Eles eram principalmente lógicos, não de acesso a dados. Vou tentar fazer alguns testes repetíveis no Natal e postar novamente aqui.
Neil McGuigan
@a_horse_with_no_name aqui está um exemplo rápido-n-sujo para FizzBuzz plv8 vs plpgsql: blog.databasepatterns.com/2014/08/plv8-vs-plpgsql.html
Neil McGuigan
8

De um modo geral, mover a lógica do aplicativo para o banco de dados significa que é mais rápido - afinal, ele estará mais próximo dos dados.

Acredito (mas não tenho 100% de certeza) que as funções da linguagem SQL sejam mais rápidas do que aquelas que usam outras linguagens, porque não requerem alternância de contexto. A desvantagem é que nenhuma lógica processual é permitida.

O PL / pgSQL é o mais completo e completo de recursos das linguagens incorporadas - mas, para desempenho, C pode ser usado (embora apenas beneficie funções computacionalmente intensivas)

Jack Douglas
fonte
7

Você pode fazer coisas muito interessantes usando as funções definidas pelo usuário (UDF) no postgresql. Por exemplo, existem dezenas de idiomas possíveis que você pode usar. O construído em pl / sql e pl / pgsql são capazes e confiáveis ​​e usam um método sandbox para impedir que os usuários façam algo terrivelmente perigoso. As UDFs escritas em C oferecem a você o melhor em desempenho e potência, uma vez que são executadas no mesmo contexto que o próprio banco de dados. No entanto, é como brincar com fogo, porque mesmo pequenos erros podem causar grandes problemas, com falhas de back-end ou dados corrompidos. As linguagens custome pl, como pl / R, pl / ruby, pl / perl, etc., fornecem a capacidade de gravar as camadas de banco de dados e de aplicativo nos mesmos idiomas. Isso pode ser útil, pois significa que você não precisa ensinar um programador perl java ou pl / pgsql etc para escrever um UDF.

Por fim, existe a linguagem pl / proxy . Essa linguagem UDF permite executar seu aplicativo em dezenas ou mais servidores backgresql de back-end para fins de dimensionamento. Foi desenvolvido pelo pessoal do Skype e basicamente permite uma solução de escala horizontal para pessoas pobres. É surpreendentemente fácil escrever também.

Agora, quanto ao problema de desempenho. Esta é uma área cinza. Você está escrevendo um aplicativo para uma pessoa? Ou para 1.000? ou por 10.000.000? A maneira como você cria seu aplicativo e usa UDFs dependerá muito de como você está tentando escalar. Se você está escrevendo para milhares e milhares de usuários, a principal coisa que você quer fazer é reduzir a carga no banco de dados, tanto quanto possível. UDFs que reduzem a quantidade de dados que estão sendo movidos e retornados ao banco de dados ajudarão a reduzir a carga de E / S. No entanto, se eles começarem a aumentar a carga da CPU, eles podem ser um problema. De um modo geral, reduzir a carga de E / S é a primeira prioridade e garantir que os UDFs sejam eficientes para não sobrecarregar sua CPU é o próximo.

Scott Marlowe
fonte