Principais considerações
Vejo uma vantagem importante para heaps e outra para tabelas em cluster, além de uma terceira consideração que pode ser feita de qualquer maneira.
Uma pilha poupa uma camada de indireção. Os índices contêm IDs de linha, apontando diretamente (bem, não realmente, mas o mais diretamente possível) para um local do disco. Portanto, uma pesquisa de índice em relação a um heap deve custar aproximadamente metade de uma pesquisa de índice não em cluster em uma tabela em cluster.
Um índice agrupado é classificado, por si só, graças a um índice (quase) livre. Como o índice de cluster é refletido na ordem física dos dados, ele ocupa relativamente pouco espaço em cima dos dados reais, o que, é claro, é necessário armazenar de qualquer maneira. Como é ordenada fisicamente, uma varredura de intervalo nesse índice pode procurar o ponto inicial e depois seguir com eficiência até o ponto final.
Os índices nos montes fazem referência aos RIDs, que são 64 bits. Como mencionado, os índices não agrupados em uma tabela em cluster referenciam a chave de cluster, que pode ser menor (32 bits INT
), igual (64 bits BIGINT
) ou maior (48 bits DATETIME2()
mais 32 bits INT
, ou um GUID de 128 bits). Obviamente, uma referência mais ampla gera índices maiores e mais caros.
Requisitos de espaço
Com estas duas tabelas:
CREATE TABLE TmpClustered
(
ID1 INT NOT NULL,
ID2 INT NOT NULL
)
ALTER TABLE TmpClustered ADD CONSTRAINT PK_Tmp1 PRIMARY KEY CLUSTERED (ID1)
CREATE UNIQUE INDEX UQ_Tmp1 ON TmpClustered (ID2)
CREATE TABLE TmpNonClustered
(
ID1 INT NOT NULL,
ID2 INT NOT NULL
)
ALTER TABLE TmpNonClustered ADD CONSTRAINT PK_Tmp2 PRIMARY KEY NONCLUSTERED (ID1)
CREATE UNIQUE INDEX UQ_Tmp2 ON TmpNonClustered (ID2)
... cada um preenchido com 8,7 M de registros, o espaço necessário era de 150 MB para os dados de ambos; 120 MB para os índices da tabela em cluster, 310 MB para os índices da tabela em cluster. Isso reflete que o índice de cluster é mais estreito que um RID e que o índice de cluster é principalmente um "brinde". Sem os índices exclusivos ID2
, o espaço de índice necessário cai para 155 MB para a tabela não agrupada em cluster (metade, como seria de esperar), mas apenas 150 KB para a PK agrupada - quase nada.
Portanto, um índice não agrupado de um campo de 32 bits em uma tabela agrupada com um índice de 32 bits (total de 64 bits, nominalmente) ocupava 120 MB, enquanto um índice de um campo de 32 bits em um heap com um de 64 bits O RID (total de 96 bits, nominalmente) ocupou 155 MB, um pouco menos do que o aumento de 50% que se esperaria ingenuamente de passar de chaves de 64 bits para 96 bits, mas é claro que há uma sobrecarga que reduz a diferença efetiva de tamanho.
Preencher as duas tabelas e criar seus índices levou o mesmo tempo para cada tabela. Executando testes simples que envolvem varreduras ou buscas, não encontrei diferenças significativas de desempenho entre as tabelas, o que corresponde ao white paper da Microsoft que o gbn vinculou de maneira útil. O referido documento mostra uma diferença significativa para acesso altamente simultâneo; Não sei por que isso acontece, espero que alguém com mais experiência do que eu em sistemas OLTP de alto volume possa nos dizer.
A adição de ~ 40 bytes de dados aleatórios de comprimento variável não alterou apreciavelmente essa equivalência. A substituição de INT
s por UUIDs amplos também não (cada tabela foi reduzida na mesma extensão). Sua milhagem pode variar, mas na maioria dos casos, se um índice está disponível é mais importante do que que tipo.
Bits e Peças
Fazer uma varredura de intervalo em um índice não clusterizado - porque a tabela é uma pilha ou o índice não é o índice clusterizado - envolve a varredura do índice e, em seguida, uma pesquisa na tabela para cada ocorrência. Isso pode ser muito caro, então às vezes é mais barato apenas digitalizar a tabela. Você pode contornar isso com um índice de cobertura, no entanto. Isso se aplica se você agrupou sua tabela ou não.
Como o @gbn apontou, não há uma maneira simples de compactar um monte. No entanto, se sua tabela aumentar gradualmente ao longo do tempo - um caso muito comum - haverá pouco desperdício, pois o espaço liberado pelas exclusões será preenchido por novos dados.
Várias das discussões de heap versus tabela em cluster que eu vi fazem um argumento curioso de que um heap sem índices é inferior a uma tabela em cluster, pois sempre exige uma varredura de tabela. Isso certamente é verdade, mas a comparação mais significativa é "tabela agrupada grande e bem indexada" versus "pilha grande e bem indexada". Se sua tabela é muito pequena ou você sempre fará varreduras de tabela, então não importa muito se você a agrupa ou não.
Como cada índice em uma tabela em cluster faz referência ao índice de cluster, eles são, na verdade, todos os índices de cobertura. Uma consulta que faça referência a uma coluna indexada e a (s) coluna (s) de cluster pode fazer uma varredura de índice sem nenhuma pesquisa de tabela. Isso geralmente não é valioso se o seu índice de clustering for uma chave sintética, mas se for uma chave comercial que você precisa recuperar de qualquer maneira, é um recurso interessante.
TL; DR
Sou um especialista em data warehouse, não um especialista em OLTP. Para tabelas de fatos, quase sempre uso um índice de cluster no campo que provavelmente precisará de varreduras de intervalo, geralmente um campo de data. Para tabelas de dimensões, agrupo no PK, para que seja pré-definido para junções de mesclagem com tabelas de fatos.
Existem vários motivos para usar índices de cluster, mas se nenhum desses motivos se aplicar, a sobrecarga poderá não valer a pena. Eu suspeito que há muitas "sempre fizemos dessa maneira" e "é apenas uma prática recomendada" por trás de pessoas que usam índices agrupados universalmente. Tente ambos com os seus dados e sua carga e ver o que funciona melhor.