BT

Disseminando conhecimento e inovação em desenvolvimento de software corporativo.

Contribuir

Tópicos

Escolha a região

Início Notícias SQL Server 2019 corrige bug nas funções Inlining

SQL Server 2019 corrige bug nas funções Inlining

Favoritos

Como a maioria dos bancos de dados criados nas últimas décadas, o SQL Server permite que os desenvolvedores estendam o banco de dados criando funções. Mas antes da próxima versão de 2019, o suporte do SQL Server para funções escalares estava repleto de problemas.

No SQL Server, uma função que retorna um único valor é conhecida como "UDF escalar" ou "função escalar definida pelo usuário". Essas UDFs escalares são automaticamente categorizadas como determinísticas ou não-determinísticas pelo banco de dados. Um exemplo de uma função não determinística seria aquela que lê a hora atual ou busca dados de uma tabela. Uma função determinística é garantida para sempre retornar o mesmo valor para um determinado conjunto de parâmetros. Em teoria, isso significa que o banco de dados pode aplicar otimizações adicionais quando sabe que uma UDF determinista está sendo usada.

Infelizmente, o SQL Server nunca integrou totalmente o suporte UDF escalar ao seu gerador de plano de execução. Isso significa que, com frequência, executaria um trabalho desnecessário, como a execução de uma função determinística em cada linha, mesmo quando estivesse claro que seria mais rápido executá-la apenas uma vez para cada valor exclusivo.

É difícil dizer com que frequência reutilizar os valores anteriores ajudaria realmente. A menos que os dados fossem pré-classificados ou caso soubesse que havia um número limitado de entradas possíveis, o custo de armazenar em cache os parâmetros e resultados da função pode ter superado o benefício. No entanto, esse não é o único problema com UDFs escalares.

Outro problema com UDFs escalares no SQL Server é que eles impedem a paralelização. A capacidade de distribuir consultas complexas em várias CPUs é um importante ponto de venda para o SQL Server. (Muitas das alternativas de software livre, mal têm suporte à paralelização ou dependem de bancos de dados distribuídos.) Sem paralelização, muitos acham difícil justificar o preço do SQL Server.

Falando em preço, o SQL Server não tem como estimar quão cara é uma função escalar. Todas as funções escalares, não importa quão simples ou complexas, recebem um único custo padrão no plano de execução.

Os UDFs escalares também são interpretados separadamente na consulta que os utiliza. Que, de acordo com a Microsoft, envolve uma mudança de contexto na consulta para a função e volta para cada linha. Pode-se assumir que o custo da troca de contexto pode exceder o custo da própria função.

Por esses motivos, muitos desenvolvedores e DBAs recomendam o uso de funções escalares para código sensível ao desempenho. O que, embora seja compreensível, muitas vezes leva a uma grande quantidade de duplicação de código, já que o conteúdo da função escalar é colado em cada consulta, visualização e procedimento armazenado que precise dela. Não seria incomum ouvir declarações como "a reutilização de código, além das visualizações, é inadequada para um banco de dados".

A penalidade de desempenho para funções escalares não pode ser exagerada. Considere esta função simples:

CREATE FUNCTION dbo.discount_price(@price DECIMAL(12,2), @discount DECIMAL(12,2))
RETURNS DECIMAL (12,2) AS
BEGIN
    RETURN @price * (1 - @discount);
END

De acordo com Karthik Ramachandra, pesquisador da Microsoft, essa função escalar pode fazer com que uma consulta normalmente leve 1,6 segundos, ao invés de exigir 29 minutos e 11 segundos. Isso é 1.000 vezes mais lento sem diferença semântica.

Existe uma solução alternativa. Em vez de usar uma função escalar, é possível usar uma "função de valor de tabela embutida" ou "TVF embutido". Uma função de valor de tabela normalmente retorna um conjunto de linhas, mas pode ser gravado para retornar apenas uma. Nesse ponto o operador CROSS APPLY pode ser usado para simular uma chamada de função escalar normal. E, como o nome indica, os TVFs inline são alinhados e otimizados normalmente.

Função inlining no SQL Server 2019

A partir do SQL Server 2019, as funções escalares escritas em T-SQL podem ser embutidas. Isso significa que elas serão dobradas na consulta e não incorrerão mais na sobrecarga da UDF. Ao olhar para o plano de execução, não se deve ver nenhuma diferença entre uma consulta com lógica colada e uma consulta usando uma função escalar inlined.

Essa nova funcionalidade não se limita a expressões simples. Alguns UDFs de instruções múltiplas também podem ser embutidos. Isso permite até mesmo inlining de UDFs não determinísticos que envolvem a leitura de dados das tabelas. Também pode inferir a necessidade de adicionar um operador JOIN ou GROUP BY para combinar as tabelas na consulta com as tabelas na função.

Nem todas as funções podem ser embutidas. Atualmente, a UDF precisa atender a esses requisitos:

  • Uma UDF T-SQL escalar pode ser inline se todas as condições a seguir forem verdadeiras:
  • O UDF pode ser escrito usando as seguintes construções:
  • DECLARE, SET: declaração de variáveis ​​e atribuições.
  • SELECT: consulta SQL com atribuições de variável única/múltipla.
  • IF/ELSE: Ramificação com níveis arbitrários de aninhamento.
  • RETURN: declarações de retorno únicas ou múltiplas.
  • UDF: chamadas de funções aninhadas / recursivas.
  • Outros: operações relacionais, como EXISTS, ISNULL.
  • A UDF não invoca nenhuma função intrínseca que seja dependente do tempo (como GETDATE()) ou que tenha efeitos colaterais (como NEWSEQUENTIALID() );
  • A UDF usa cláusula EXECUTE AS CALLER (o comportamento padrão se a cláusula EXECUTE AS não for especificada);
  • A UDF não faz referência a variáveis ​​de tabela ou parâmetros com valor de tabela;
  • A consulta que chama uma UDF escalar não faz referência a uma chamada UDF escalar em sua cláusula GROUP BY;
  • A UDF não é compilada nativamente (interop é suportado);
  • A UDF não é usada em uma coluna computada ou em uma definição de restrição de verificação;
  • A UDF não faz referência a tipos definidos pelo usuário;
  • Não há assinaturas adicionadas a UDF;
  • A UDF não é uma função de partição.

Para cada UDF escalar de T-SQL, a exibição de catálogo sys.sql_modules inclui uma propriedade chamada is_inlineable, que indica se uma UDF é inlineable ou não. Um valor de 1 indica que é inlineable e 0 indica o contrário. Esta propriedade terá um valor de 1 para todos os TVFs em linha também. Para todos os outros módulos, o valor será 0.

O inline UDF pode ser desativado configurando o nível de compatibilidade do banco de dados para menos de 150 ou definindo a configuração com escopo denominada TSQL_SCALAR_UDF_INLINING para OFF.

O inlining também pode ser desabilitado para uma determinada consulta usando OPTION (USE HINT ('DISABLE_TSQL_SCALAR_UDF_INLINING')).

Finalmente, é possível especificar que uma função específica nunca deve ser inlined usando WITH INLINE = OFF em sua declaração.

Froid e oportunidades futuras

Essa funcionalidade pode nunca ter sido adicionada ao SQL Server sem o projeto de pesquisa do Froid. Publicado sob o título, Froid: Otimização de Programas Imperativos em uma Base de Dados Relacionais nos Anais do VLDB Endowment e na 44ª Conferência Internacional sobre Bases de Dados Muito Grandes no Rio de Janeiro, Brasil. Froid é descrito como:

uma estrutura extensível para otimizar programas imperativos em bancos de dados relacionais. A nova abordagem de Froid transforma automaticamente as Funções Definidas pelo Usuário (UDFs) em expressões algébricas relacionais e as incorpora na consulta SQL de chamada. Essa forma é agora passível de otimização baseada em custo e resulta em planos paralelos eficientes e orientados para o conjunto, em oposição à execução serial, ineficiente e iterativa de UDFs. A abordagem de Froid também traz os benefícios de muitas otimizações de compiladores para UDFs sem nenhum esforço adicional de implementação. Descrevemos o design de Froid e apresentamos nossa avaliação experimental que demonstra melhorias de desempenho de até várias ordens de grandeza em cargas de trabalho reais.

Atualmente, o framework Froid é conhecido apenas por suportar o T-SQL, mas o C#, o Java, o Python e o R também foram mencionados no artigo acadêmico. Com o SQL Server agora suportando três das quatro linguagens, seria benéfico estender a função inline para as outras linguagens.

Avalie esse artigo

Relevância
Estilo/Redação

Conteúdo educacional

HTML é permitido: a,b,br,blockquote,i,li,pre,u,ul,p

HTML é permitido: a,b,br,blockquote,i,li,pre,u,ul,p

BT