Existe algum benefício plausível para o iterador de spool no primeiro plano?
Isso depende do que você considera "plausível", mas a resposta de acordo com o modelo de custo é sim. Claro que isso é verdade, porque o otimizador sempre escolhe o plano mais barato que encontra.
A verdadeira questão é por que o modelo de custo considera o plano com o carretel muito mais barato que o plano sem. Considere os planos estimados criados para uma tabela nova (a partir do seu script) antes que quaisquer linhas tenham sido adicionadas ao repositório delta:
DELETE Fact.RecordedMetricsDetail
WHERE MeasurementTime < DATEADD(day,-1,GETUTCDATE())
OPTION (RECOMPILE);
O custo estimado para este plano é de 771.734 unidades :
O custo está quase todo associado à Exclusão de Índice em Cluster, porque é esperado que as exclusões resultem em uma grande quantidade de E / S aleatória. Essa é apenas a lógica genérica que se aplica a todas as modificações de dados. Por exemplo, presume-se que um conjunto não-ordenado de modificações em um índice de árvore-b resulte em E / S amplamente aleatória, com um alto custo de E / S associado.
Os planos de alteração de dados podem apresentar uma Classificação para apresentar linhas em uma ordem que promoverá o acesso sequencial, exatamente por esses motivos de custo. O impacto é exacerbado neste caso porque a tabela está particionada. Muito particionado, de fato; seu script cria 15.000 deles. As atualizações aleatórias em uma tabela muito particionada têm um custo especialmente alto, já que o preço para alternar as partições (conjuntos de linhas) no meio do fluxo também recebe um alto custo.
O último fator importante a ser considerado é que a consulta de atualização simples acima (onde 'atualização' significa qualquer operação de alteração de dados, incluindo uma exclusão) se qualifica para uma otimização chamada "compartilhamento de conjunto de linhas", onde o mesmo conjunto de linhas interno é usado para varredura e atualizando a tabela. O plano de execução ainda mostra dois operadores separados, mas, no entanto, há apenas um conjunto de linhas usado.
Menciono isso porque ser capaz de aplicar essa otimização significa que o otimizador adota um caminho de código que simplesmente não considera os benefícios potenciais da classificação explícita para reduzir o custo de E / S aleatória. Onde a tabela é uma árvore b, isso faz sentido, porque a estrutura é inerentemente ordenada; portanto, o compartilhamento do conjunto de linhas fornece todos os benefícios potenciais automaticamente.
A consequência importante é que a lógica de custo para o operador de atualização não considera esse benefício de pedido (promoção de E / S sequenciais ou outras otimizações) em que o objeto subjacente é o armazenamento de colunas. Isso ocorre porque as modificações do armazenamento de coluna não são executadas no local; eles usam uma loja delta. O modelo de custo está, portanto, refletindo a diferença entre atualizações de conjuntos de linhas compartilhadas em b-trees e colunas.
No entanto, no caso especial de um columnstore particionado (muito!), Ainda pode haver um benefício no pedido preservado, pois executar todas as atualizações em uma partição antes de passar para a próxima ainda pode ser vantajoso do ponto de vista de E / S .
A lógica de custo padrão é reutilizada para armazenamentos de colunas aqui, portanto, um plano que preserva a ordem das partições (embora não seja dentro de cada partição) tem um custo menor. Podemos ver isso na consulta de teste usando o sinalizador de rastreamento não documentado 2332 para exigir entrada classificada para o operador de atualização. Isso define a DMLRequestSort
propriedade como true na atualização e força o otimizador a produzir um plano que forneça todas as linhas para uma partição antes de passar para a próxima:
DELETE Fact.RecordedMetricsDetail
WHERE MeasurementTime < DATEADD(day,-1,GETUTCDATE())
OPTION (RECOMPILE, QUERYTRACEON 2332);
O custo estimado para este plano é muito menor, em 52.5174 unidades:
Essa redução no custo deve-se ao menor custo estimado de E / S na atualização. O spool introduzido não executa nenhuma função útil, exceto para garantir a saída na ordem da partição, conforme exigido pela atualização DMLRequestSort = true
(a varredura serial de um índice de armazenamento de coluna não pode fornecer essa garantia). O custo do spool em si é considerado relativamente baixo, especialmente em comparação com a redução (provavelmente irrealista) no custo na atualização.
A decisão sobre a necessidade de solicitar entrada ordenada ao operador de atualização é tomada muito cedo na otimização da consulta. As heurísticas usadas nesta decisão nunca foram documentadas, mas podem ser determinadas por tentativa e erro. Parece que o tamanho de qualquer loja delta é uma entrada para essa decisão. Uma vez feita, a escolha é permanente para a compilação da consulta. Nenhuma USE PLAN
dica será bem-sucedida: o objetivo do plano solicitou entrada para a atualização ou não.
Há outra maneira de obter um plano de baixo custo para essa consulta sem limitar artificialmente a estimativa de cardinalidade. Uma estimativa suficientemente baixa para evitar o spool provavelmente resultará em DMLRequestSort sendo falso, resultando em um custo planejado muito alto devido à E / S aleatória esperada. Uma alternativa é usar o sinalizador de rastreamento 8649 (plano paralelo) em conjunto com 2332 (DMLRequestSort = true):
DELETE Fact.RecordedMetricsDetail
WHERE MeasurementTime < DATEADD(day,-1,GETUTCDATE())
OPTION (RECOMPILE, QUERYTRACEON 2332, QUERYTRACEON 8649);
Isso resulta em um plano que usa a varredura paralela no modo de lote por partição e uma troca Gather Streams que preserva a ordem (mesclando):
Dependendo da eficácia em tempo de execução do pedido de partição no seu hardware, isso pode ter um desempenho melhor dos três. Dito isso, grandes modificações não são uma boa idéia no armazenamento de colunas, portanto a idéia de troca de partição é quase certamente melhor. Se você conseguir lidar com os longos tempos de compilação e as opções de plano peculiar, geralmente vistas com objetos particionados - especialmente quando o número de partições é grande.
Combinar muitos recursos relativamente novos, especialmente perto de seus limites, é uma ótima maneira de obter planos de execução ruins. A profundidade do suporte ao otimizador tende a melhorar com o tempo, mas o uso de 15.000 partições do armazenamento de colunas provavelmente sempre significará que você vive em momentos interessantes.
OPTION (QUERYRULEOFF EnforceHPandAccCard)
o carretel desaparece. Presumo que a HP possa ser "Proteção do Dia das Bruxas". No entanto, a tentativa de usar esse plano com umaUSE PLAN
dica falha (assim como a tentativa de usar o planoOPTIMIZE FOR
também)