Por que o otimizador escolheria Índice clusterizado + Classificação em vez de Índice não clusterizado?

11

Dado o próximo exemplo:

IF OBJECT_ID('dbo.my_table') IS NOT NULL
    DROP TABLE [dbo].[my_table];
GO

CREATE TABLE [dbo].[my_table]
(
    [id]    int IDENTITY (1,1)  NOT NULL PRIMARY KEY,
    [foo]   int                 NULL,
    [bar]   int                 NULL,
    [nki]   int                 NOT NULL
);
GO

/* Insert some random data */
INSERT INTO [dbo].[my_table] (foo, bar, nki)
SELECT TOP (100000)
    ABS(CHECKSUM(NewId())) % 14,
    ABS(CHECKSUM(NewId())) % 20,
    n = CONVERT(INT, ROW_NUMBER() OVER (ORDER BY s1.[object_id]))
FROM 
    sys.all_objects AS s1 
CROSS JOIN 
    sys.all_objects AS s2
GO

CREATE UNIQUE NONCLUSTERED INDEX [IX_my_table]
    ON [dbo].[my_table] ([nki] ASC);
GO

Se eu buscar todos os registros ordenados por [nki](Índice sem cluster):

SET STATISTICS TIME ON;
SELECT id, foo, bar, nki FROM my_table ORDER BY nki;
SET STATISTICS TIME OFF;

SQL Server Execution Times: CPU time = 266 ms, elapsed time = 493 ms

O otimizador escolhe o índice em cluster e aplica um algoritmo de classificação.

insira a descrição da imagem aqui

Execution plan

Mas se eu forçá-lo a usar o índice não agrupado:

SET STATISTICS TIME ON;
SELECT id, foo, bar, nki FROM my_table WITH(INDEX(IX_my_TABLE));
SET STATISTICS TIME OFF;

SQL Server Execution Times: CPU time = 311 ms, elapsed time = 188 ms

Em seguida, ele usa índice não clusterizado com uma pesquisa de chave:

insira a descrição da imagem aqui

Execution plan

Obviamente, se o índice não agrupado em cluster for transformado em um índice de cobertura:

CREATE UNIQUE NONCLUSTERED INDEX [IX_my_table]
    ON [dbo].[my_table] ([nki] ASC)
    INCLUDE (id, foo, bar);
GO

Em seguida, ele usa apenas este índice:

SET STATISTICS TIME ON;
SELECT id, foo, bar, nki FROM my_table ORDER BY nki;
SET STATISTICS TIME OFF;

SQL Server Execution Times: CPU time = 32 ms, elapsed time = 106 ms

insira a descrição da imagem aqui

Execution plan


Questão

  • Por que o SQL Server usa o índice clusterizado mais um algoritmo de classificação em vez de usar um índice não clusterizado, mesmo que o tempo de execução seja 38% mais rápido no último caso?
McNets
fonte
11
Você queria deixar de fora o ORDER BY na sua consulta de índice forçado?
Forrest

Respostas:

9

Por que o SQL Server usa o índice clusterizado mais um algoritmo de classificação em vez de usar um índice não clusterizado, mesmo que o tempo de execução seja 38% mais rápido no último caso?

Como o SQL Server usa um otimizador baseado em custos com base em estatísticas, não em informações de tempo de execução.

Durante o processo de estimativa de custos para esta consulta, ele realmente avalia o plano de pesquisa, mas estima que será necessário mais esforço. (Observe o "Custo estimado da subárvore" ao passar o mouse sobre SELECT no plano de execução). Isso também não é necessariamente uma suposição ruim - na minha máquina de teste, o plano de pesquisa leva 6X a CPU da classificação / varredura.

Veja a resposta de Rob Farley sobre por que o SQL Server pode custar mais caro ao plano de pesquisa.

Forrest
fonte
9

Se você comparar o número de leituras necessárias em 100.000 pesquisas com o que está envolvido em uma classificação, poderá ter uma ideia rápida sobre por que o Query Optimizer calcula que a Classificação CIX + seria a melhor opção.

A execução da Pesquisa acaba sendo mais rápida porque as páginas lidas estão na memória (mesmo que você limpe o cache, você tem muitas linhas por página, portanto, você está lendo as mesmas páginas repetidamente, mas com diferentes quantidades de fragmentação ou pressão de memória diferente de outra atividade, pode não ser o caso). Realmente não seria preciso muito para que o CIX + Sort fosse mais rápido, mas o que você está vendo é que o custo de uma leitura não leva em consideração o custo relativo de acessar as mesmas páginas repetidamente.

Rob Farley
fonte
4

Decidi me aprofundar um pouco nessa questão e descobri alguns documentos interessantes falando sobre como e quando usar ou talvez melhor, não forçar o uso de um índice não agrupado.

Conforme sugerido pelos comentários de John Eisbrener , um dos blogs mais citados , mesmo em outros blogs, é este artigo interessante de Kimberly L. Tripp:

mas não é o único, se você estiver interessado, consulte estas páginas:

Como você pode ver, todos eles se movem em torno do conceito do ponto de tombamento .

Citado no artigo da KL Tripp

Qual é o ponto de inflexão?

É o ponto em que o número de linhas retornadas " não é mais seletivo o suficiente ". O SQL Server escolhe NÃO usar o índice não agrupado para procurar as linhas de dados correspondentes e, em vez disso, executa uma varredura de tabela.

Quando o SQL Server usa um índice não agrupado em cluster, basicamente ele obtém uma lista de ponteiros para as páginas da tabela base. Em seguida, ele usa esses ponteiros para recuperar as linhas com uma série de operações denominadas RID (pesquisas de identificação de linha). Isso significa que, pelo menos, usará tantas leituras de página quanto o número de linhas retornadas, e talvez mais. O processo é um pouco semelhante com um índice clusterizado como a tabela base, com o mesmo resultado: mais leituras.

Mas quando esse ponto de inflexão ocorre?

Claro que, como a maioria das coisas nesta vida, depende ...

Não é sério, ocorre entre 25% e 33% do número de páginas na tabela, dependendo de quantas linhas por página. Mas há mais fatores que você deve considerar:

Citado no artigo ITPRoToday

Outros fatores que afetam o ponto de inflexão Embora o custo das pesquisas de RID seja o fator mais importante que afeta o ponto de inflexão, existem vários outros fatores:

  • A E / S física é muito mais eficiente ao varrer um índice em cluster. Os dados do índice em cluster são colocados seqüencialmente no disco na ordem do índice. Consequentemente, há muito pouco deslocamento da cabeça lateral no disco, o que melhora o desempenho de E / S.
  • Quando o mecanismo de banco de dados está varrendo um índice em cluster, ele sabe que há uma alta probabilidade de que as próximas páginas na trilha de disco ainda contenham os dados necessários. Portanto, ele começa a ler adiante em blocos de 64 KB, em vez das páginas normais de 8 KB. Isso também resulta em E / S mais rápidas.

Agora, se eu executar minhas consultas novamente usando estatísticas IO:

SET STATISTICS IO ON;
SELECT id, foo, bar, nki FROM my_table WHERE nki < 20000 ORDER BY nki ;
SET STATISTICS IO OFF;

Logical reads: 312

SET STATISTICS IO ON;
SELECT id, foo, bar, nki FROM my_table WITH(INDEX(IX_my_TABLE));
SET STATISTICS IO OFF;

Logical reads: 41293

A segunda consulta precisa de mais leituras lógicas que a primeira.

Devo evitar o índice não agrupado?

Não, um índice em cluster pode ser útil, mas vale a pena dedicar tempo e fazer um esforço extra analisando o que você está tentando obter com ele.

Citado no artigo da KL Tripp

Então o que você deveria fazer? Depende. Se você conhece bem seus dados e faz alguns testes extensivos, pode considerar usar uma dica (existem algumas coisas inteligentes que você pode fazer programaticamente em sps, tentarei dedicar uma publicação a isso em breve). No entanto, uma escolha muito melhor (se possível) é considerar a cobertura (esse é realmente o meu ponto principal :). Nas minhas consultas, a cobertura é irreal, porque minhas consultas desejam todas as colunas (o SELECT incorreto *), mas, se suas consultas são mais estreitas E de alta prioridade, é melhor ter um índice de cobertura (em muitos casos) sobre uma dica, porque um índice que cobre uma consulta, nunca dicas.

Essa é a resposta do quebra-cabeça por enquanto, mas há definitivamente muito mais para se aprofundar. O ponto de inflexão pode ser uma coisa muito boa - e geralmente funciona bem. Mas, se você achar que pode forçar um índice e obter um melhor desempenho, poderá investigar e ver se é isso. Depois, considere a probabilidade de uma dica ajudar e agora você sabe onde pode se concentrar.

McNets
fonte