Por que o operador de concatenação estima menos linhas do que suas entradas?


20

No seguinte fragmento do plano de consulta, parece óbvio que a estimativa de linha para o Concatenationoperador deve ser ~4.3 billion rowsou a soma das estimativas de linha para suas duas entradas.

No entanto, ~238 million rowsé produzida uma estimativa , levando a uma subótima Sort/ Stream Aggregateestratégia que derrama centenas de GB de dados em tempdb. Uma estimativa logicamente consistente nesse caso teria produzido a Hash Aggregate, removido o derramamento e melhorado drasticamente o desempenho da consulta.

Isso é um bug no SQL Server 2014? Existem circunstâncias válidas em que uma estimativa menor do que as entradas possa ser razoável? Quais soluções alternativas podem estar disponíveis?

insira a descrição da imagem aqui

Aqui está o plano de consulta completo (anonimizado). Não tenho acesso sysadmin a esse servidor para fornecer saídas QUERYTRACEON 2363ou sinalizadores de rastreamento semelhantes, mas talvez seja possível obter essas saídas de um administrador, se elas forem úteis.

O banco de dados está no nível de compatibilidade 120 e, portanto, está usando o novo Estimador de Cardinalidade do SQL Server 2014.

As estatísticas são atualizadas manualmente sempre que os dados são carregados. Dado o volume de dados, atualmente estamos usando a taxa de amostragem padrão. É possível que uma taxa de amostragem mais alta (ou FULLSCAN) possa ter um impacto.

Respostas:


21

Para citar Campbell Fraser neste item do Connect :

Essas "inconsistências de cardinalidade" podem surgir em várias situações, inclusive quando a concat é usada. Eles podem surgir porque a estimativa de uma subárvore específica no plano final pode ter sido realizada em uma subárvore de estrutura diferente, mas logicamente equivalente. Devido à natureza estatística da estimativa de cardinalidade, não é garantido que a estimativa em árvores diferentes, mas logicamente equivalentes, obtenha a mesma estimativa. Portanto, no geral, não são fornecidas garantias da consistência esperada.

Para expandir um pouco: A maneira como eu gosto de explicar é dizer que a estimativa inicial de cardinalidade (realizada antes do início da otimização baseada em custo) produz estimativas de cardinalidade mais "consistentes", uma vez que toda a árvore inicial é processada, com cada subseqüente estimativa dependendo diretamente da anterior.

Durante a otimização baseada em custos, partes da árvore do plano (um ou mais operadores) podem ser exploradas e substituídas por alternativas, cada uma das quais pode exigir uma nova estimativa de cardinalidade. Não existe uma maneira geral de dizer qual estimativa será geralmente melhor que outra, portanto é bem possível terminar com um plano final que pareça "inconsistente". Isso é simplesmente o resultado da junção de "pedaços de planos" para formar o arranjo final.

Tudo isso dito, houve algumas alterações detalhadas no novo estimador de cardinalidade (CE) introduzido no SQL Server 2014 que tornam isso um pouco menos comum do que no caso do CE original.

Além de atualizar para a Atualização Cumulativa mais recente e verificar se as correções do otimizador com 4199 estão ativadas, suas principais opções são tentar alterações de estatísticas / índices (observando os avisos de falta de índices) e atualizações, ou expressar a consulta de maneira diferente. O objetivo é adquirir um plano que exiba o comportamento que você precisa. Isso pode ser congelado com um guia de plano, por exemplo.

O plano anonimizado dificulta a avaliação dos detalhes, mas eu também examinaria cuidadosamente os bitmaps para ver se eles são da variedade 'otimizado' (Opt_Bitmap) ou pós-otimização (Bitmap). Também desconfio dos filtros.

Se as contagens de linhas forem precisas, isso parece uma consulta que pode se beneficiar do columnstore. Além dos benefícios usuais, você pode tirar proveito da concessão de memória dinâmica para operadores de modo em lote (o sinalizador de rastreamento 9389 pode ser necessário).


7

A criação de uma cama de teste reconhecidamente bastante simples no SQL Server 2012 (11.0.6020) permite recriar um plano com duas consultas correspondentes a hash sendo concatenadas por meio de a UNION ALL. Minha cama de teste não exibe a estimativa incorreta que você vê. Talvez este seja um problema do SQL Server 2014 CE.

Eu recebo uma estimativa de 133.785 linhas para uma consulta que realmente retorna 280 linhas, no entanto, isso é esperado, como veremos mais adiante:

IF OBJECT_ID('dbo.Union1') IS NOT NULL
DROP TABLE dbo.Union1;
CREATE TABLE dbo.Union1
(
    Union1_ID INT NOT NULL
        CONSTRAINT PK_Union1
        PRIMARY KEY CLUSTERED
        IDENTITY(1,1)
    , Union1_Text VARCHAR(255) NOT NULL
    , Union1_ObjectID INT NOT NULL
);

IF OBJECT_ID('dbo.Union2') IS NOT NULL
DROP TABLE dbo.Union2;
CREATE TABLE dbo.Union2
(
    Union2_ID INT NOT NULL
        CONSTRAINT PK_Union2
        PRIMARY KEY CLUSTERED
        IDENTITY(2,2)
    , Union2_Text VARCHAR(255) NOT NULL
    , Union2_ObjectID INT NOT NULL
);

INSERT INTO dbo.Union1 (Union1_Text, Union1_ObjectID)
SELECT o.name, o.object_id
FROM sys.objects o;

INSERT INTO dbo.Union2 (Union2_Text, Union2_ObjectID)
SELECT o.name, o.object_id
FROM sys.objects o;
GO

SELECT *
FROM dbo.Union1 u1
    INNER HASH JOIN sys.objects o ON u1.Union1_ObjectID = o.object_id
UNION ALL
SELECT *
FROM dbo.Union2 u2
    INNER HASH JOIN sys.objects o ON u2.Union2_ObjectID = o.object_id;

Eu acho que o motivo está relacionado à falta de estatísticas para as duas junções resultantes que são UNIONADAS. O SQL Server precisa adivinhar, na maioria dos casos, a seletividade das colunas diante da falta de estatísticas.

Joe Sack tem uma leitura interessante sobre isso aqui .

Para um UNION ALL, é seguro dizer que veremos exatamente o número total de linhas retornadas por cada componente da união, no entanto, como o SQL Server está usando estimativas de linha para os dois componentes do UNION ALL, vemos que ele adiciona o total estimado de linhas de ambos consultas para apresentar a estimativa para o operador de concatenação.

No meu exemplo acima, o número estimado de linhas para cada parte do UNION ALLé 66,8927, que quando somados é igual a 133.785, o que vemos para o número estimado de linhas para o operador de concatenação.

O plano de execução real da consulta de união acima se parece com:

insira a descrição da imagem aqui

Você pode ver o número "estimado" vs "real" de linhas. No meu caso, adicionar o número "estimado" de linhas retornadas pelos dois operadores de correspondência de hash é exatamente igual à quantidade mostrada pelo operador de concatenação.

Gostaria de tentar obter saída do rastreamento 2363 etc, conforme recomendado no post de Paul White, que você mostra na sua pergunta. Como alternativa, você pode tentar usar OPTION (QUERYTRACEON 9481)a consulta para reverter para a versão 70 CE e verificar se isso "corrige" o problema.


11
Obrigado. Definitivamente, vi que "o motivo é a falta de estatísticas para as duas junções resultantes que são UNIONADAS" ter um grande impacto nas junções ou agregações subsequentes (que ocorrem após a UNION). O SQL 2014 realmente lida com isso melhor do que o SQL 2012 na minha experiência. Aqui está um script de teste simples que usei no passado, por exemplo: gist.github.com/anonymous/1497112d8b25ab8fb782a04569959c68 No entanto, eu não acho que um operador de concatenação precisaria do mesmo tipo de informação sobre a distribuição de valores que uma associação pode precisar.
Geoff Patterson

Concordo com você que a concatenação não deve precisar de estatísticas para executar com precisão. Ele deve simplesmente ser capaz de adicionar de forma confiável as estimativas de linhas recebidas para ter uma boa noção do número de linhas que serão exibidas. Como o @PaulWhite mostra em sua resposta, surpreendentemente nem sempre é o caso. Para mim, a ideia aqui é que pode parecer simples, mas na realidade pode não ser. Estou muito feliz por você ter feito a pergunta da maneira que fez. Só queria que você não tivesse que anonimizar o plano - teria sido interessante ver a consulta real.
Max Vernon
Ao utilizar nosso site, você reconhece que leu e compreendeu nossa Política de Cookies e nossa Política de Privacidade.
Licensed under cc by-sa 3.0 with attribution required.