SQL Server: Cobrindo índices, incluindo todas as colunas?

9

Nossa equipe herdou um aplicativo e um banco de dados associado. Os desenvolvedores anteriores parecem ter aplicado uma regra em que todo índice, em todas as tabelas, possui uma cláusula INCLUDE para sempre adicionar todas as colunas que não fazem parte da chave. Essas tabelas têm em média de dois a cinco índices ou restrições exclusivas, além de chaves estrangeiras.

A intenção é melhorar o desempenho do SELECT, independentemente da consulta lançada no banco de dados, pois o acesso é por meio de um ORM que, por padrão (mas nem sempre) recupera todas as colunas. Esperamos que os efeitos colaterais disso sejam maiores requisitos de armazenamento (possivelmente muito) e tempo adicional adicional para INSERT / UPDATE / DELETE.

A questão é: essa é uma estratégia sensata? Nossa equipe tem histórico com o SQL Server, mas não há membros que se considerem especialistas em seu comportamento interno (embora tenha sido levantada a questão de que, se essa estratégia fosse ótima, não seria o padrão agora?). Que outros efeitos colaterais (uso da CPU / memória / TempDB do servidor de banco de dados, etc.) deveríamos esperar ou algumas de nossas suposições acima estão incorretas?

Além disso, o aplicativo pode ser instalado no SQL Server local (versões desde 2012) e no Azure SQL - devemos estar preparados para diferenças entre os dois ou efeitos colaterais adicionais no Azure, como resultado disso aproximação?

T2PS
fonte

Respostas:

8

Eu já fiz isso em índices específicos antes de agora, para ajudar muitas vezes a executar consultas pesadas. Efetivamente, o que eles fizeram foi criar vários índices em cluster: quando qualquer um desses índices é usado para encontrar linhas, não é necessário trabalho extra procurando o restante dos dados no índice em cluster real (ou no heap se não houver índice em cluster real) .

Esta é uma estratégia sensata?

Para alguns índices, quando necessário, para suportar determinados padrões de consulta, certamente sim.

Mas, para fazer isso com todos os índices, eu certamente diria que não.

Vai ser um desperdício de espaço para fazer onde não é realmente necessário e reduzirá significativamente as inserções / atualizações. Isso pode diminuir a velocidade de consultas de leitura, pois ajuda também, porque cada página de índice contém menos registros; portanto, qualquer consulta que precise fazer referência a uma parte do índice para filtragem, mas não usar todas as outras colunas, precisará acessar mais páginas. Isso tornará seu banco de dados com mais memória: essas páginas precisarão ser carregadas no buffer pool, ejetando outras páginas úteis se houver pouca memória. Se a compactação for usada nesses índices para tentar mitigar o efeito nos requisitos de armazenamento e memória, ela estará enviando uma carga extra para as CPUs.

como o acesso é via ORM que, por padrão (mas nem sempre) recupera todas as colunas

Esse é um padrão comum com o uso pouco otimizado de um ORM (ou apenas ORMs ingênuos) e, nesses casos, eu vi o consultor de índices do SQL Server (e ferramentas similares de terceiros) sugerir índices com muitas INCLUDEcolunas d, então eu concordo com o seu sugestão de que é por isso que os índices foram criados dessa maneira.

Mas, embora possa tornar essas consultas um pouco mais rápidas e algumas significativamente mais rápidas, suspeito que, em muitos casos, qualquer benefício seja tão pequeno que não valha o espaço extra de memória exigido pelo seu conjunto de trabalho comum, o espaço no disco e o IO entre disco e memória.

Lembre-se também de que o ORM pode não estar selecionando todas as colunas de todas as tabelas que uma consulta toca, para que o benefício seja válido apenas para o destino principal da solicitação atual, e os índices maiores podem penalizar a consulta quando outros objetos são usados ​​para filtrar mas não retornando dados ( SELECT * FROM table1 WHERE id IN (SELECT someID FROM table2 WHERE someColumn='DesiredValue')talvez).

Outra consideração pelo excesso de espaço usado, especialmente se os dados forem grandes, é que eles terão um impacto na sua estratégia de backup: custos de armazenamento e transferência para esses backups, possíveis tempos de restauração e assim por diante.

devemos estar preparados para quaisquer diferenças entre os dois [no local e AzureSQL]

Geralmente, acho que as considerações aqui serão as mesmas em cada caso, embora qualquer excesso de custo de memória / IO imposto pelos grandes índices possa ser mais diretamente visível no Azure, onde você pode ajustar a camada de serviço e, portanto, a infraestrutura custa mais facilmente do que tendo um conjunto relativamente fixo de recursos de hardware. Se você usar camadas padrão / premium em vez de preços baseados em vcore, você será afetado mais pelo custo de IO no padrão, pois o premium inclui significativamente mais IO por DTU. Se você estiver usando backups com várias regiões ou redundância ou outros recursos não locais no Azure, poderá haver um custo de largura de banda associado ao espaço extra ocupado por índices desnecessariamente amplos.

David Spillett
fonte
Fomos em frente e fizemos essa remoção. Um efeito colateral foi o de que em determinadas tabelas, SELECTsem especificar, ORDER BYcomeçaram a retornar as mesmas linhas de antes, mas com uma ordem arbitrária diferente.
T2PS 12/04/19
Isso não é inesperado. A ordem dos resultados sem 'ORDER BY' é por definição indefinida e pode mudar a qualquer momento que o planejador de consultas decida adotar uma abordagem diferente, o que pode ser resultado de alterações no índice ou nos padrões de dados à medida que cresce. Outros fatores podem fazer essa alteração de pedido posteriormente, mesmo sem essa alteração. Se você depende da ordem de saída de uma instrução, mesmo que superficialmente, é necessário incluir um 'ORDER BY' para garantir isso.
precisa saber é o seguinte
Definitivamente. O comentário anterior foi mais um lembrete para quem encontrar essa resposta mais tarde.
T2PS 15/04/19
5

A questão é: essa é uma estratégia sensata?

Na maioria dos casos, essa não é uma estratégia sensata. O motivo é que, em bancos de dados OLTP gerais, as linhas retornadas ao usuário final não serão muito. (Generalização)

A pergunta que você deve se perguntar é: se você estiver procurando nas colunas-chave, quantas linhas serão retornadas por essa operação de busca? E repita isso para as consultas que procuram nessa coluna.

Considere a tabela a seguir, retornando várias colunas, where SelectiveIDField= ...

select columnA,columnC, ... columnZ
FROM dbo.BigTable
Where SelectiveIDField= '225122141';

Se apenas uma linha for retornada pela busca selectiveIDField, a pesquisa de chave adicional é uma coisa tão ruim? (supondo que você tenha agrupado índices aqui, caso contrário, pesquisa RID)

Ele fará apenas uma pesquisa de chave extra, uma execução extra + o operador de junção. Mesmo que seja 10 ou mesmo 100, seria um impacto tão grande? Isso também depende do quanto sua consulta é executada e da importância do tempo de execução.

No caso de ser insignificante, basta criar o índice SelectiveIDFielde chamá-lo por dia, não deve valer os ganhos de leitura em comparação com as perdas de gravação.

Portanto, em resumo, criar índices em toda a tabela não deve ser uma abordagem padrão, a menos que você realmente veja um problema com uma consulta e possa melhorá-lo drasticamente adicionando um índice de cobertura inteiro.

Randi Vertongen
fonte