Remus apontou que o comprimento máximo da VARCHAR
coluna 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 256
na 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 LOG
arquivo. 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 :)
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?
Plano de consulta real: VARCHAR (256)
No plano de consulta, podemos ver que o Sort
operador 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.)
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 Scan
e Sort
são processados sem derramamentos ( spill_page_count
permanece por 0
toda parte).
Na VARCHAR(256)
versão, no entanto, podemos ver que os derramamentos de página se acumulam rapidamente para o Sort
operador. 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.
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.
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.