A declaração da volatilidade da função IMMUTABLE pode prejudicar o desempenho?

9

As funções do Postgres são declaradas com classificação de volatilidade VOLATILE, STABLEouIMMUTABLE . Sabe-se que o projeto é muito rigoroso com esses rótulos para funções internas. E por uma boa razão. Exemplo proeminente: os índices de expressão permitem apenas IMMUTABLEfunções e essas precisam ser realmente imutáveis ​​para evitar resultados incorretos.

As funções definidas pelo usuário ainda estão livres para serem declaradas como o proprietário escolher. O manual aconselha:

Para obter melhores resultados de otimização, você deve rotular suas funções com a categoria de volatilidade mais estrita que é válida para elas.

... e adiciona uma extensa lista de coisas que podem dar errado com um rótulo de volatilidade incorreto.

Ainda assim, há casos em que fingir imutabilidade faz sentido. Principalmente quando você sabe que a função é imutável dentro do seu escopo. Exemplo:

Além de todas as possíveis implicações na integridade dos dados , qual é o efeito no desempenho? Pode-se supor que declarar uma função IMMUTABLEsó pode ser benéfico para o desempenho . É assim mesmo?

A declaração da volatilidade da função pode IMMUTABLE prejudicar o desempenho?

Vamos assumir o Postgres 10 atual para reduzi-lo, mas todas as versões recentes são de interesse.

Erwin Brandstetter
fonte
11
Como uma observação lateral também, todo o "verdadeiramente imutável" nos índices de expressão é uma verdadeira pita. Essa é uma interface horrível. Deveríamos ser capazes de FORCEeles de qualquer maneira. 100% dos DBAs experientes do PostgreSQL mentem para contornar essa interface do usuário com funções de wrapper. Pelo menos FORCE, não precisaríamos de invólucros e não precisaríamos mentir sobre a volatilidade declarada do fn.
Evan Carroll
11
Suponho que FORCEé suposto fazer com que os índices de expressão aceitem funções não imutáveis ​​(enquanto os marcam como possíveis pontos de falha). Sim, isso pareceria uma solução mais elegante do que invólucros imutáveis ​​de funções.
Erwin Brandstetter
Não sei quase nada sobre o PostGres, mas a volatilidade não é redundante? O que isso significa? Sério, não espere que isso seja confiável, porque é loucura ?
Anthony
@ Anthony: Esclareci um pouco mais. Siga o link do manual para obter detalhes.
Erwin Brandstetter

Respostas:

7

Sim, isso pode prejudicar o desempenho.

Funções SQL simples podem ser "incorporadas" na consulta de chamada. Citando o Wiki do Postgres :

As funções SQL (ou seja LANGUAGE SQL), em determinadas condições, terão seus corpos de função embutidos na consulta de chamada, em vez de serem invocados diretamente. Isso pode ter vantagens substanciais de desempenho, uma vez que o corpo da função fica exposto ao planejador da consulta de chamada, que pode aplicar otimizações como dobragem constante, empilhamento qualificado e assim por diante.

Negrito ênfase minha.

Para impor a correção, existem várias pré-condições. Um deles :

se a função for declarada IMMUTABLE, a expressão não deverá chamar nenhuma função ou operador não imutável

Ou seja, funções SQL que usam funções não imutáveis, mas ainda sendo declaradas, IMMTUTABLEsão excluídas dessa otimização. Acionado por essas respostas relacionadas ao SO, tenho executado extensos testes:

Basicamente, comparando essas duas variantes de uma função SQL simples (mapear datas para um integer, ignorando o ano que não importa para o efeito):

CREATE FUNCTION f_mmdd_tc_s(date) RETURNS int LANGUAGE sql STABLE    AS
$$SELECT to_char($1, 'MMDD')::int$$;

CREATE FUNCTION f_mmdd_tc_i(date) RETURNS int LANGUAGE sql IMMUTABLE AS
$$SELECT to_char($1, 'MMDD')::int$$;  -- cannot be inlined!

A função Postgres to_char()é apenas STABLE, não IMMUTABLE(todas as instâncias sobrecarregadas dela - por razões fora do escopo desta resposta ). Portanto, o segundo é falso IMMUTABLEe acaba sendo 5x mais lento em um teste simples:

db <> mexer aqui

Este exemplo específico pode ser substituído pelo equivalente:

CREATE FUNCTION f_mmdd(date) RETURNS int LANGUAGE sql IMMUTABLE AS
$$SELECT (EXTRACT(month FROM $1) * 100 + EXTRACT(day FROM $1))::int$$;

Será que parecem mais caro com duas chamadas de função e mais cálculos. Mas o IMMUTABLErótulo é verdadeira (mais, a função usada é mais rápido e coagir texta integeré mais caro, também).

2x mais rápido que a variante mais rápida acima (10x mais rápido que o mais lento). A questão é: use IMMUTABLEfunções sempre que possível , para não ter que "trapacear" para começar.

Erwin Brandstetter
fonte
Descobertas legais! Tenha um acompanhamento imediato: dba.stackexchange.com/q/212198/2639 #
Evan Carroll
Você sabe o que acho que perdi aqui, que não sabia. Isso STABLEtambém é inline. Eu pensei que o otimizador IMMUTABLEfuncionaria apenas online .
Evan Carroll
VOLATILEtão bem.
Erwin Brandstetter
O wiki diz a função é estável declarada ou IMMUTABLE wiki.postgresql.org/wiki/Inlining_of_SQL_functions
Evan Carroll
.. em "Condições internas para funções da tabela ". Não para funções escalares. Eu demonstrei isso no violino: dbfiddle.uk/…
Erwin Brandstetter