Diferença de desempenho entre o índice clusterizado e não clusterizado

22

Eu estava lendo Clusterede Non Clustered Indexes.

Clustered Index- Ele contém páginas de dados. Isso significa que as informações completas da linha estarão presentes na coluna Índice agrupado.

Non Clustered Index- Ele contém apenas as informações do Localizador de linhas na forma de coluna Índice clusterizado (se disponível) ou o Identificador de arquivo + Número da página + Linhas totais em uma página. Isso significa que o mecanismo de consulta deve executar uma etapa adicional para localizar os dados reais.

Consulta - Como posso verificar a diferença de desempenho com a ajuda de um exemplo prático, pois sabemos que a tabela pode ter apenas um Clustered Indexe fornece sortingno Clustered Index Columne Non Clustered Indexnão fornece sortinge pode suportar 999 Non Clustered Indexespol SQL Server 2008e 249 pol SQL Server 2005.


fonte
2
Diferença de desempenho quando você faz o quê ?, que tipo de trabalho que você quer fazer com que a tabela ?, não há uma solução única que se adapte a cada necessidade
Lamak
2
Alguma discussão tangível aqui, talvez. stackoverflow.com/questions/91688/… stackoverflow.com/questions/5070529/… stackoverflow.com/questions/1251636/… Poderíamos escrever uma dissertação sobre as diferenças entre índices agrupados e não agrupados, mas acho que não diria qualquer coisa que ainda não esteja disponível para você ler.
Aaron Bertrand
4
Você escreveu: "Isso significa que o mecanismo de consulta deve executar uma etapa adicional para localizar os dados reais". Na verdade, se tudo o que você precisa são colunas abordadas no índice , você não precisa executar nenhuma etapa adicional depois de encontrar as linhas de destino no índice não clusterizado. Somente quando você precisar de colunas não cobertas pelo índice não clusterizado, o SQL Server precisará executar uma pesquisa de marcador .
Nick Chammas

Respostas:

43

Muito boa pergunta, pois é um conceito tão importante. Este é um tópico importante e o que vou mostrar é uma simplificação para que você possa entender os conceitos básicos.

Em primeiro lugar, quando você vê a tabela de índice de cluster . No SQL server, se uma tabela não contém um índice em cluster, é uma pilha. Criar um índice em cluster na tabela na verdade transforma a tabela em uma estrutura do tipo b-tree. Seu índice clusterizado É sua tabela, não está separado da tabela

Você já se perguntou por que você pode ter apenas um índice em cluster? Bem, se tivéssemos dois índices agrupados, precisaríamos de duas cópias da tabela. Afinal, ele contém os dados.

Vou tentar explicar isso usando um exemplo simples.

NOTA: Criei a tabela neste exemplo e a preenchi com mais de 3 milhões de entradas aleatórias. Em seguida, executou as consultas reais e colou os planos de execução aqui.

O que você realmente precisa entender é notação O ou eficiência operacional . Vamos supor que você tenha a tabela a seguir.

CREATE TABLE [dbo].[Customer](
[CustomerID] [int] IDENTITY(1,1) NOT NULL,
[CustomerName] [varchar](100) NOT NULL,
[CustomerSurname] [varchar](100) NOT NULL,
CONSTRAINT [PK_Customer] PRIMARY KEY CLUSTERED 
(
[CustomerID] ASC
)WITH (PAD_INDEX  = OFF, STATISTICS_NORECOMPUTE  = OFF
  , IGNORE_DUP_KEY = OFF,ALLOW_ROW_LOCKS  = ON
  , ALLOW_PAGE_LOCKS  = ON) ON [PRIMARY]
) ON [PRIMARY]

Portanto, aqui temos uma tabela básica com uma chave agrupada no Código do Cliente (a chave primária é agrupada por padrão). Assim, a tabela é organizada / ordenada com base na chave primária CustomerID. Os níveis intermediários conterão os valores CustomerID. As páginas de dados conterão a linha inteira, portanto, é a linha da tabela.

Também criaremos um índice não agrupado no campo CustomerName. O código a seguir fará isso.

CREATE NONCLUSTERED INDEX [ix_Customer_CustomerName] ON [dbo].[Customer] 
 (
[CustomerName] ASC
 )WITH (PAD_INDEX  = OFF, STATISTICS_NORECOMPUTE  = OFF
  , SORT_IN_TEMPDB = OFF, IGNORE_DUP_KEY = OFF
  , DROP_EXISTING = OFF, ONLINE = OFF
  , ALLOW_ROW_LOCKS  = ON, ALLOW_PAGE_LOCKS  = ON) ON [PRIMARY]

Portanto, neste índice, você encontrará nas páginas de dados / nós no nível da folha um ponteiro para os níveis intermediários no índice em cluster. O índice é organizado / ordenado em torno do campo CustomerName. Portanto, o nível intermediário contém os valores CustomerName e o nível da folha conterá o ponteiro (esses valores de ponteiro são, na verdade, os valores da chave primária ou a coluna CustomerID).

Certo, se executarmos a seguinte consulta:

SELECT * FROM Customer WHERE CustomerID = 1 

O SQL provavelmente lerá o índice em cluster por meio de uma operação de busca. Uma operação de busca é uma pesquisa binária que é muito mais eficiente do que uma varredura que é uma pesquisa seqüencial. Portanto, no exemplo acima, o índice é lido e, usando uma pesquisa binária, o SQL pode eliminar os dados que não correspondem aos critérios que estamos procurando. Veja a captura de tela anexada para o plano de consulta.

insira a descrição da imagem aqui

Portanto, o número de operações ou Notação O para a operação de busca é o seguinte:

  1. Faça uma pesquisa binária no índice clusterizado comparando o valor pesquisado com os valores no nível intermediário.
  2. Retorne os valores correspondentes (lembre-se de que o índice clusterizado possui todos os dados, pode retornar todas as colunas do índice, pois são os dados da linha)

Então são duas operações. No entanto, se executarmos a seguinte consulta:

SELECT * FROM Customer WHERE CustomerName ='John'

O SQL agora usará o índice não clusterizado no CustomerName para fazer a pesquisa. No entanto, como esse é um índice sem cluster, ele não contém todos os dados na linha.

Portanto, o SQL fará a pesquisa nos níveis intermediários para encontrar os registros correspondentes e faça uma pesquisa usando os valores retornados para fazer outra pesquisa no índice clusterizado (também conhecido como tabela) para recuperar os dados reais. Isso parece confuso, eu sei, mas continue a ler e tudo ficará claro.

Como nosso índice não agrupado em cluster contém apenas o campo CustomerName (os valores do campo indexado armazenados nos nós intermediários) e o ponteiro para os dados que são o CustomerID, o índice não possui registro do CustomerSurname. O CustomerSurname deve ser buscado no índice ou tabela em cluster.

Ao executar esta consulta, recebo o seguinte plano de execução:

insira a descrição da imagem aqui

Há duas coisas importantes para você notar na captura de tela acima

  1. O SQL está dizendo que tenho um índice ausente (o texto em verde). O SQL está sugerindo que eu crie um índice em CustomerName que inclua CustomerID e CustomerSurname.
  2. Você também verá que 99% do tempo da consulta é gasto em uma pesquisa de chave no índice de chave primária / índice em cluster.

Por que o SQL está sugerindo o índice no CustomerName novamente? Bem, já que o índice contém apenas o CustomerID e o CustomerName SQL ainda precisa encontrar o CustomerSurname na tabela / índices agrupados.

Se criamos o índice e incluímos a coluna CustomerSurname no índice, o SQL poderá satisfazer a consulta inteira apenas lendo o índice não agrupado em cluster. É por isso que o SQL está sugerindo que eu mude meu índice não agrupado em cluster.

Aqui você pode ver a operação extra que o SQL precisa fazer para obter a coluna CustomerSurname da chave em cluster

Assim, o número de operações é o seguinte:

  1. Faça pesquisa binária no índice não em cluster comparando o valor pesquisado com os valores no nível intermediário
  2. Para nós que correspondem, leia o nó no nível da folha que conterá o ponteiro para os dados no índice em cluster (os nós no nível da folha conterão os valores da chave primária a propósito).
  3. Para cada valor retornado, faça uma leitura no índice clusterizado (a tabela) para obter os valores da linha aqui, leríamos o CustomerSurname.
  4. Retornar linhas correspondentes

São 4 operações para obter os valores. O dobro da quantidade de operações necessárias em comparação com a leitura do índice em cluster. O programa mostra que seu índice clusterizado é o índice mais poderoso, pois contém todos os dados.

Então, apenas para esclarecer um último ponto. Por que digo que o ponteiro no índice não agrupado é o valor da chave primária? Bem, para demonstrar que os nós no nível folha do índice não clusterizado contêm o valor da chave primária, altero minha consulta para:

SELECT CustomerID
FROM Customer
WHERE CustomerName='Jane'

Nesta consulta, o SQL pode ler o Código do Cliente no índice não clusterizado. Não é necessário fazer uma pesquisa no índice clusterizado. Isso você pode ver pelo plano de execução que se parece com isso.

insira a descrição da imagem aqui

Observe a diferença entre esta consulta e a consulta anterior. Não há pesquisa. SQL pode encontrar todos os dados no índice não agrupado em cluster

Esperamos que você possa começar a entender que o índice clusterizado é a tabela e os índices não clusterizados NÃO contêm todos os dados. A indexação acelerará as seleções devido ao fato de que pesquisas binárias podem ser feitas, mas apenas índices agrupados contêm todos os dados. Portanto, uma pesquisa em um índice não clusterizado quase sempre resultará no carregamento de valores do índice clusterizado. Essas operações extras tornam os índices não agrupados em cluster menos eficientes que um índice agrupado.

Espero que isso esclareça as coisas. Se algo não fizer sentido, poste um comentário e tentarei esclarecer. É bastante tarde aqui e meu cérebro está um pouco tenso. Hora de um touro vermelho.

Namíbia
fonte
Eu tenho uma pergunta. Por que a pesquisa que um índice procura no índice não clusterizado em CustomerName para esta consulta SELECT * FROM Customer WHERE CustomerName = 'John'. Como é um índice não clusterizado, o nome do usuário personalizado não será classificado. Portanto, não deve ser feita uma verificação de índice.
Ckv
BTW Ótima resposta totalmente entendida, exceto a pergunta acima.
Ckv #
1
Um índice é classificado na ordem dos dados. Por exemplo, ele seria classificado em Nome do cliente, pois é o valor indexado. Então está ordenado. Lembre-se de que ainda é necessário digitalizar o nível ou as páginas da folha.
Namphibian
9

"Isso significa que o mecanismo de consulta deve executar uma etapa adicional para localizar os dados reais."

Não necessariamente - se o índice estiver cobrindo uma determinada consulta, nenhuma viagem deverá ser feita nas páginas de dados. Além disso, com as colunas incluídas, colunas adicionais podem ser adicionadas a um índice não agrupado para torná-lo coberto sem alterar o tamanho da chave.

Portanto, a resposta final é - Depende (de muito mais informações do que você realmente pode cobrir em uma única pergunta) - você precisa entender todos os recursos dos índices e o plano de execução de uma determinada consulta pode divergir das suas expectativas.

Uma regra geral prática que tenho é que uma tabela sempre possui um índice em cluster (e geralmente em uma identidade ou GUID seqüencial), mas os índices não em cluster são adicionados para desempenho. Mas sempre há exceções - as tabelas de heap têm um lugar, índices clusterizados mais amplos têm um lugar. Os índices aparentemente redundantes, mais estreitos para caber em mais linhas por página, têm um lugar. etc etc.

E eu não me preocuparia com os limites dos vários índices permitidos - isso quase certamente não entrará em jogo em muitos exemplos do mundo real.

Cade Roux
fonte
2
+1 para there are always exceptions- muitas pessoas omitem isso e pensam que todo índice agrupado deve ser um int identityque seja.
JNK