Database
 sql >> Base de Dados >  >> RDS >> Database

Ajuste de desempenho do joelho-empurrão:uso incorreto de tabelas temporárias


Nesta continuação da minha série "ajuste de desempenho instintivo", gostaria de discutir quatro problemas comuns que vejo com o uso de tabelas temporárias. Qualquer um desses problemas pode prejudicar uma carga de trabalho, portanto, vale a pena conhecê-los e procurá-los em seu ambiente.

Problema 1:usando tabelas temporárias onde elas não são necessárias

https://www.flickr. com/photos/tea_time/3890677277/
As tabelas temporárias têm uma variedade de usos (provavelmente o mais comum é armazenar um conjunto de resultados intermediário para uso posterior), mas é preciso lembrar que, ao introduzir uma tabela temporária em uma consulta, você está interrompendo o fluxo de dados através do processador de consultas.

Pense na população de uma tabela temporária como uma parada difícil, pois há uma consulta (vamos chamá-la de produtor) para produzir o conjunto de resultados intermediário, que é armazenado na tabela temporária em tempdb e, em seguida, a próxima consulta (vamos chamar é o consumidor) tem que ler os dados da tabela temporária novamente.

Muitas vezes descobri que algumas partes de uma carga de trabalho realmente funcionam melhor quando a tabela temporária é completamente removida, de modo que os dados fluem da parte do produtor da consulta para a parte do consumidor da consulta sem precisar persistir no tempdb e o O otimizador de consulta pode produzir um plano geral mais otimizado.

Você pode estar pensando agora:"então, por que alguém usaria uma tabela temporária se isso torna as coisas mais lentas?" – e com razão! Em casos como esse, descobri que o uso de uma tabela temporária se tornou institucionalizado na equipe de desenvolvimento; alguém descobriu que usar uma tabela temporária aumentava o desempenho há muitos anos, então as tabelas temporárias se tornaram a escolha de design padrão.

Isso pode ser difícil de mudar, especialmente se você tiver um desenvolvedor ou gerente sênior convencido de que as tabelas temporárias devem sempre ser usadas. A coisa simples a tentar é escolher uma consulta cara (por exemplo, uma de longa duração ou executada muitas vezes por segundo) e remover uma ou mais das tabelas temporárias para ver se o desempenho aumenta sem elas. E se sim, aí está sua prova para mostrar aos intransigentes!

Problema 2:falta de filtragem ao preencher tabelas temporárias


Mesmo se você não puder remover uma tabela temporária, poderá melhorar drasticamente o desempenho certificando-se de que o código que preenche a tabela temporária esteja filtrando corretamente os dados extraídos das tabelas de origem.

Perdi a conta do número de vezes que vi uma tabela temporária sendo preenchida com código que começa como SELECT * , inclui algumas junções irrestritas e não tem cláusula WHERE e, em seguida, a consulta posterior que usa a tabela temporária usa apenas algumas colunas e tem uma cláusula WHERE para reduzir enormemente o número de linhas.

Lembro-me de um caso em que uma tabela temporária em um procedimento armazenado estava agregando 15 anos de dados do banco de dados principal e, em seguida, apenas os dados do ano atual estavam sendo usados. Isso fazia com que o tempdb aumentasse repetidamente até ficar sem espaço no volume do disco e o procedimento armazenado falharia.

Sempre que você estiver preenchendo uma tabela temporária, use apenas as colunas da tabela de origem que são necessárias e use apenas as linhas que são necessárias – ou seja, envie os predicados do filtro para cima no código de preenchimento da tabela temporária. Isso não apenas economizará espaço no tempdb, mas também economizará muito tempo por não ter que copiar dados desnecessários da tabela de origem (e potencialmente remover a necessidade de ler as páginas do banco de dados de origem do disco em primeiro lugar).

Problema 3:Indexação de tabela temporária incorreta


Assim como nas tabelas normais, você deve criar apenas os índices que realmente serão usados ​​pelo código de consulta posterior para ajudar no desempenho da consulta. Já vi muitos casos em que há um índice não clusterizado por coluna de tabela temporária e índices de coluna única que são escolhidos sem analisar o código posterior geralmente são bastante inúteis. Agora combine índices não clusterizados inúteis com a falta de filtragem ao preencher a tabela temporária e você terá uma receita para um enorme inchaço de tempdb.

Além disso, em geral, é mais rápido criar os índices depois que a tabela for preenchida. Isso oferece o bônus adicional de que os índices terão estatísticas precisas, o que pode ajudar ainda mais a consulta, pois o otimizador de consulta poderá fazer uma estimativa precisa de cardinalidade.

Ter um monte de índices não clusterizados que não são usados ​​desperdiça não apenas espaço em disco, mas também o tempo necessário para criá-los. Se isso estiver em um código executado com frequência, a remoção desses índices desnecessários que são criados toda vez que o código é executado pode ter um efeito significativo no desempenho geral.

Problema 4:contenção de trava de tempdb


É bastante comum haver um gargalo de travamento no tempdb que pode ser rastreado até o uso temporário da tabela. Se houver muitas conexões simultâneas executando código que cria e elimina tabelas temporárias, o acesso aos bitmaps de alocação do banco de dados na memória pode se tornar um gargalo significativo.

Isso ocorre porque apenas um thread por vez pode estar alterando um bitmap de alocação para marcar páginas (da tabela temporária) como alocadas ou desalocadas e, portanto, todos os outros threads precisam esperar, diminuindo a taxa de transferência da carga de trabalho. Embora haja um cache de tabela temporária desde o SQL Server 2005, ele não é muito grande e há restrições sobre quando a tabela temporária pode ser armazenada em cache (por exemplo, somente quando tem menos de 8 MB de tamanho).

As formas tradicionais de contornar esse problema têm sido usar o sinalizador de rastreamento 1118 e vários arquivos de dados tempdb (consulte esta postagem no blog para obter mais informações), mas outra coisa a considerar é remover completamente as tabelas temporárias!

Resumo


As tabelas temporárias podem ser muito úteis, mas são muito fáceis e comumente usadas incorretamente. Sempre que você estiver escrevendo (ou revisando código) usando uma tabela temporária, considere o seguinte:
  • Esta tabela temporária é realmente necessária ?
  • É o código que preenche a tabela usando a filtragem correta limitar o tamanho da tabela temporária?
  • Os índices são criados após o preenchimento da tabela (em geral) e são os índices usados por código posterior?

Paul White tem alguns ótimos posts (aqui e aqui) sobre uso de objetos temporários e cache que eu recomendo ler também.

E uma última coisa, se você decidir não usar uma tabela temporária, não a substitua apenas por uma variável de tabela, uma expressão de tabela comum ou um cursor (todos são maneiras comuns pelas quais as pessoas tentam "otimizar" o tabela temporária) – descubra a maneira mais eficiente de (re)escrever o código – não há uma resposta "tamanho único".

Até a próxima, feliz solução de problemas!