reconstruir no índice clusterizado, por que o tamanho de dados diminui?

10

Quando fizemos uma reconstrução em um índice agrupado em uma tabela com cerca de 15 GB de dados e o tamanho dos dados diminuiu para 5 GB, como pode ser isso? Que tipo de "dados" é removido?

Tamanho dos dados, quero dizer a coluna "data" do DBCC sp_spaceused

Antes de reconstruir no índice em cluster:

name                  rows        reserved    data        index_size  unused
LEDGERJOURNALTRANS    43583730    39169656 KB 15857960 KB 22916496 KB 395200 KB

Após reconstruir no índice clusterizado:

name                  rows        reserved    data        index_size  unused
LEDGERJOURNALTRANS    43583730    29076736 KB 5867048 KB  22880144 KB 329544 KB

TSQL para reconstrução:

USE [DAX5TEST]
GO
ALTER INDEX [I_212RECID] ON [dbo].[LEDGERJOURNALTRANS] REBUILD PARTITION = ALL WITH ( PAD_INDEX  = OFF, STATISTICS_NORECOMPUTE  = OFF, ALLOW_ROW_LOCKS  = ON, ALLOW_PAGE_LOCKS  = ON, ONLINE = ON, SORT_IN_TEMPDB = OFF, DATA_COMPRESSION = PAGE, FILLFACTOR = 85 )
GO
Daniel Björk
fonte
Você está determinando o tamanho dos dados no tamanho do arquivo?
JNK
O tamanho dos dados i significa os "dados" em coluna de DBCC sp_spaceused
Daniel Bjork
Essa seria a coluna "dados" de EXEC sp_spaceused.
RLF 29/09
11
Todo corpo não percebeu que o OP está usando a compactação de página = habilitada em seu script de reconstrução e acho que não era antes. Daniel você pode confirmar?
Shanky
11
@ Shanky: Essa ALTER INDEXdeclaração parece que foi gerada por código (como inclui um monte de opções em sua configuração padrão), então eu suspeito que ela foi criada a partir das opções existentes no índice. Mas você está certo: se a compactação não tiver sido ativada no índice clusterizado antes da execução, isso definitivamente explicaria a maior parte da redução na pegada de dados. (mais uma vez: Daniel, você poderia confirmar uma forma ou de outra?)
David Spillett

Respostas:

16

Quando uma tabela possui um índice em cluster, o índice são os dados da tabela (caso contrário, você possui uma tabela do tipo heap). Uma reconstrução do índice clusterizado (qualquer índice de fato, mas o espaço não seria contado como "dados" para um índice não clusterizado) resultará na mesclagem de páginas parcialmente usadas em uma forma mais completa.

À medida que você insere dados em um índice (agrupado ou não) na ordem do índice, as páginas folha são criadas conforme necessário e você terá apenas uma página parcial: a que está no final. Quando você insere os dados fora da ordem do índice, uma página precisa ser dividida para que os dados se encaixem no lugar certo: você termina com duas páginas que estão aproximadamente pela metade e a nova linha entra em uma delas. Com o tempo, isso pode acontecer bastante, consumindo bastante espaço extra, embora, em certa medida, as inserções futuras preencham algumas das lacunas. As páginas que não são folhas também terão um efeito semelhante, mas as páginas de dados reais são muito mais significativas em tamanho do que são.

Também as exclusões podem resultar em páginas parciais. Se você remover todas as linhas de uma página, elas serão contadas como "não utilizadas", mas, se houver uma ou mais linhas de dados, ainda serão contadas como em uso. Mesmo se houver apenas uma linha usando 10 bytes em uma página, essa página conta como 8192 bytes na contagem de espaço usado. Novamente, inserções futuras podem preencher parte dessa lacuna.

Para linhas de comprimento variável, as atualizações também podem ter o mesmo efeito: à medida que uma linha fica menor, pode deixar espaço em sua página que não é fácil de reutilizar posteriormente, e se uma linha em uma página quase inteira crescer mais, poderá forçar a divisão da página .

O SQL Server não gasta tempo tentando normalizar os dados reorganizando a maneira como as páginas são usadas, até que seja explicitamente informado a sua ordem de reconstrução do índice, pois esses exercícios de coleta de lixo podem ser um pesadelo no desempenho.

Eu suspeito que isso é o que você está vendo, embora eu diria que ter espaço suficiente alocado para ~ 2,7 vezes a quantidade absolutamente necessária para os dados é um caso particularmente ruim. Pode implicar que você tenha algo aleatório como uma das chaves significativas no índice (talvez uma coluna UUID), o que significa que é improvável que novas linhas sejam adicionadas na ordem do índice e / ou que um número significativo de exclusões tenha acontecido recentemente.

Exemplo de divisão de página

Inserindo na ordem do índice com linhas de comprimento fixo, das quais quatro cabem em uma página:

Start with one empty page: 
        [__|__|__|__]
Add the first item in index order:
        [00|__|__|__]
Add the next three
        [00|02|04|06]
Adding the next will result in a new page:
        [00|02|04|06] [08|__|__|__]
And so on...
        [00|02|04|06] [08|10|12|14] [16|18|__|__]

Agora, para adicionar linhas fora da ordem do índice (é por isso que usei números pares apenas acima): Adicionar 11significaria estender a segunda página (não é possível porque são de tamanho fixo), movendo tudo acima de 11 para cima uma (muito caro em um índice grande) ou dividir a página da seguinte forma:

[00|02|04|06] [08|10|11|__] [12|14|__|__] [16|18|__|__]

A partir daqui, adicionar 13e 17não resultará em uma divisão, pois há espaço nas páginas relevantes:

[00|02|04|06] [08|10|11|__] [12|13|14|__] [16|17|18|__]

mas adicionar 03 irá:

[00|02|03|__] [04|06|__|__] [08|10|11|__] [12|13|14|__] [16|17|18|__]

Como você pode ver, após essas operações de inserção, atualmente temos 5 páginas de dados alocadas que podem caber um total de 20 linhas, mas só temos 14 linhas lá ("desperdiçando" 30% do espaço).

Uma reconstrução com opções padrão (veja abaixo sobre "fator de preenchimento") resultaria em:

[00|02|03|04] [06|08|10|11] [12|13|14|16] [17|18|__|__]

salvando uma página neste exemplo simples. É fácil ver como as exclusões podem ter um efeito semelhante às inserções de pedidos fora do índice.

Mitigação

Se você espera que os dados cheguem em uma ordem bastante aleatória em relação à ordem do índice, você pode usar a FILLFACTORopção ao criar ou recriar um índice para instruir o SQL Server a deixar lacunas artificialmente para preenchimento posterior - reduzindo as divisões da página a longo prazo, mas ocupando mais espaço inicialmente. É claro que errar esse valor pode piorar as coisas, em vez de melhorar a situação; portanto, lide com cuidado.

A divisão de páginas, principalmente no índice clusterizado, pode ter uma implicação no desempenho de inserções / atualizações, por isso FILLFACTORàs vezes é aprimorada por esse motivo, em vez do problema de uso de espaço em bancos de dados que exibem muita atividade de gravação (mas para a maioria dos aplicativos, onde as leituras superam as gravações por várias ordens de grandeza, geralmente é melhor deixar o fator de preenchimento em 100%, exceto em casos específicos, como onde você tem índices sobre colunas com conteúdo efetivamente aleatório).

Suponho que outros DBs de grande nome tenham uma opção semelhante, se você precisar desse nível de controle também.

Atualizar

Em relação à ALTER INDEXdeclaração adicionada à pergunta depois que comecei a digitar o acima: Presumo que as opções sejam as mesmas de quando o índice foi criado (ou reconstruído pela última vez), mas, se não, a opção de compactação pode ser muito significativa se for adicionada esta tempo ao redor. Também nessa declaração, o fator de preenchimento é definido como 85% e não 100%, para que cada página de folha fique ~ 15% vazia imediatamente após a reconstrução.

David Spillett
fonte
2
+1 Se o fator de preenchimento da página for menor que 100%, por exemplo, se o fator de preenchimento da página for 50%, o índice clusterizado recém-reconstruído (a tabela ) seria duas vezes maior do que se tivesse sido reconstruído com 100% do fator de preenchimento.
Max Vernon
6

Quando você reconstrói um índice, ele literalmente coloca todos os dados em novas páginas. O que eu suspeito que aconteceu foi que você removeu muitos dados antes da reconstrução, por exemplo, removeu uma coluna, atualizou uma coluna de largura variável para ter menos dados, alterou um tamanho de coluna de largura fixa ou excluiu muitas linhas. Qualquer uma dessas operações poderia deixar muito espaço vazio nas páginas, que não seriam recuperadas até a reconstrução. A coluna "dados" sp_spaceusednão está medindo os dados reais, mas o número de 8K páginas usadas para armazenar os dados. Agora, essas páginas estão mais cheias devido à reconstrução; portanto, a mesma quantidade de dados é ajustada em um número menor de páginas.

Aaron Bertrand
fonte
5

O sp_spaceusedprocedimento armazenado não está examinando o tamanho culmulativo total das linhas no banco de dados. Ele está relatando o tamanho do espaço alocado para manter esses dados no tamanho cumulativo das extensões alocadas para os dados.

Se houver espaço livre significativo disponível, como em muitas linhas excluídas, uma reconstrução do índice clusterizado compactará o espaço em páginas e extensões para ser mais eficiente (ou seja, menor) por motivos de desempenho.

Portanto, nenhum dado deveria ter sido descartado, mas o processo de reconstrução tornou disponível novamente o espaço livre incorporado nas páginas de dados.

RLF
fonte