Por que está demorando mais para criar um índice depois que o tamanho da coluna aumenta?

18

Nosso fornecedor alterou as larguras das colunas em quase todas as colunas do banco de dados inteiro. O banco de dados tem cerca de 7 TB, mais de 9000 tabelas. Estamos tentando criar um índice em uma tabela que possui 5,5 bilhões de linhas. Antes da atualização do fornecedor, poderíamos criar o índice em 2 horas. Agora leva dias. O que eles fizeram é aumentar qualquer tamanho de varchar (xx) para varchar (256). Portanto, a maioria das colunas costumava ser varchar (18) ou varchar (75) etc.

De qualquer forma, a chave primária consiste em 6 colunas que combinavam largura com 126 caracteres. Agora, após a atualização, a chave primária é 1283 caracteres, o que viola o limite de 900 caracteres dos SQL Servers. Toda a largura da coluna da tabela passou de uma contagem total de varchar combinada de 1049 para uma contagem total de varchar combinada de 4009.

Não há um aumento nos dados, a tabela não ocupa mais "espaço" do que antes de toda a largura da coluna aumentar, mas o desempenho para criar algo tão simples quanto um índice está demorando bastante tempo.

Alguém pode explicar por que está demorando muito mais para criar e indexar quando a única coisa feita era aumentar o tamanho das colunas?

O índice que estamos tentando criar não tem cluster, pois o pk é o índice em cluster. Após várias tentativas de criar o índice, desistimos. Eu acho que durou 4 ou 5 dias sem conclusão.

Tentei isso em um ambiente de não produção, obtendo uma captura instantânea do sistema de arquivos e instalei o banco de dados em um servidor mais silencioso.

Daniel Miller
fonte

Respostas:

12

Remus apontou que o comprimento máximo da VARCHARcoluna afeta o tamanho estimado da linha e, portanto, a memória concede ao SQL Server.

Tentei fazer um pouco mais de pesquisa para expandir a parte "dessa cascata de coisas" "de sua resposta. Não tenho uma explicação completa ou concisa, mas eis o que encontrei.

Repro script

Criei um script completo que gera um conjunto de dados falso no qual a criação do índice leva aproximadamente 10 vezes mais tempo na minha máquina para a VARCHAR(256)versão. Os dados utilizado é exactamente a mesma, mas a primeira tabela usa os comprimentos reais max de 18, 75, 9, 15, 123, e 5, ao mesmo tempo todas as colunas usar um comprimento máximo de 256na segunda tabela.


Como digitar a tabela original

Aqui, vemos que a consulta original é concluída em cerca de 20 segundos e as leituras lógicas são iguais ao tamanho da tabela ~1.5GB(195K páginas, 8K por página).

-- CPU time = 37674 ms,  elapsed time = 19206 ms.
-- Table 'testVarchar'. Scan count 9, logical reads 194490, physical reads 0
CREATE CLUSTERED INDEX IX_testVarchar
ON dbo.testVarchar (s1, s2, s3, s4)
WITH (MAXDOP = 8) -- Same as my global MAXDOP, but just being explicit
GO


Codificando a tabela VARCHAR (256)

Para a VARCHAR(256)tabela, vemos que o tempo decorrido aumentou drasticamente.

Curiosamente, nem o tempo da CPU nem as leituras lógicas aumentam. Isso faz sentido, uma vez que a tabela possui exatamente os mesmos dados, mas não explica por que o tempo decorrido é muito mais lento.

-- CPU time = 33212 ms,  elapsed time = 263134 ms.
-- Table 'testVarchar256'. Scan count 9, logical reads 194491
CREATE CLUSTERED INDEX IX_testVarchar256
ON dbo.testVarchar256 (s1, s2, s3, s4)
WITH (MAXDOP = 8) -- Same as my global MAXDOP, but just being explicit
GO


Estatísticas de E / S e espera: original

Se capturarmos um pouco mais detalhadamente (usando o p_perfMon, um procedimento que escrevi ), podemos ver que a grande maioria da E / S é realizada no LOGarquivo. Vemos uma quantidade relativamente modesta de E / S no atual ROWS(o principal arquivo de dados), e o principal tipo de espera é LATCH_EX, indicando a contenção da página na memória.

Também podemos ver que meu disco giratório está entre "ruim" e "chocantemente ruim", de acordo com Paul Randal :)

insira a descrição da imagem aqui


Estatísticas de E / S e espera: VARCHAR (256)

Para a VARCHAR(256)versão, as estatísticas de E / S e espera parecem completamente diferentes! Aqui vemos um grande aumento na E / S no arquivo de dados ( ROWS), e os tempos de estagnação agora fazem Paul Randal simplesmente dizer "WOW!".

Não é de surpreender que o tipo de espera nº 1 seja agora IO_COMPLETION. Mas por que tanta E / S é gerada?

insira a descrição da imagem aqui


Plano de consulta real: VARCHAR (256)

No plano de consulta, podemos ver que o Sortoperador tem um derramamento recursivo (5 níveis de profundidade!) Na VARCHAR(256)versão da consulta. (Não há nenhum derramamento na versão original.)

insira a descrição da imagem aqui


Progresso da consulta ao vivo: VARCHAR (256)

Podemos usar sys.dm_exec_query_profiles para visualizar o progresso da consulta ao vivo no SQL 2014+ . Na versão original, todo Table Scane Sortsão processados ​​sem derramamentos ( spill_page_countpermanece por 0toda parte).

Na VARCHAR(256)versão, no entanto, podemos ver que os derramamentos de página se acumulam rapidamente para o Sortoperador. Aqui está uma captura instantânea do andamento da consulta antes da conclusão da consulta. Os dados aqui são agregados em todos os segmentos.

insira a descrição da imagem aqui

Se eu cavar cada segmento individualmente, vejo que dois segmentos completam a classificação em aproximadamente 5 segundos (20 segundos no total, após 15 segundos gastos na verificação da tabela). Se todos os encadeamentos progredissem nessa taxa, a VARCHAR(256)criação do índice seria concluída aproximadamente no mesmo tempo que a tabela original.

No entanto, os 6 threads restantes progridem a uma taxa muito mais lenta. Isso pode dever-se à maneira como a memória é alocada e à maneira como os encadeamentos estão sendo mantidos pela E / S enquanto estão derramando dados. Eu não tenho certeza, no entanto.

insira a descrição da imagem aqui


O que você pode fazer?

Há várias coisas que você pode considerar tentar:

  • Trabalhe com o fornecedor para reverter para uma versão anterior. Se isso não for possível, deixe o fornecedor que você não está satisfeito com essa alteração, para que ele possa revertê-la em uma versão futura.
  • Ao adicionar o índice, considere o uso OPTION (MAXDOP X)onde Xé um número menor do que a configuração de nível de servidor atual. Quando usei OPTION (MAXDOP 2)esse conjunto de dados específico em minha máquina, a VARCHAR(256)versão foi concluída em 25 seconds(comparada a 3-4 minutos com 8 threads!). É possível que o comportamento de derramamento seja exacerbado por um paralelismo mais alto.
  • Se houver possibilidade de investimento adicional em hardware, analise a E / S (o provável gargalo) em seu sistema e considere usar um SSD para reduzir a latência da E / S incorrida por derramamentos.


Leitura adicional

Paul White tem uma boa postagem no blog sobre os tipos internos do SQL Server que podem ser interessantes. Ele fala um pouco sobre derramamento, distorção de segmento e alocação de memória para tipos paralelos.

Geoff Patterson
fonte
11

A tabela de classificação intermediária será estimada de maneira diferente entre os dois casos. Isso levará a diferentes solicitações de concessão de memória ( VARCHAR(256)serão maiores) e provavelmente a uma concessão real muito menor, em termos percentuais, em comparação com a solicitação 'ideal'. Eu acho que isso leva a derramamentos durante a classificação.

Testando o script de Geoff (apenas em 100k linhas), vejo claramente a diferença no tamanho estimado da linha (141B vs. 789B). Disso em cascata de coisas.

Remus Rusanu
fonte
8
Tenho certeza que Paul provará uma resposta mais completa e completa, incluindo talvez pilhas de chamadas, que serão usadas como material de aprendizado pela equipe de desenvolvimento. Mais uma vez ...
Remus Rusanu