A ordem das colunas em um índice PK importa?

33

Eu tenho algumas mesas muito grandes com a mesma estrutura básica. Cada um tem uma coluna RowNumber (bigint)e DataDate (date). Os dados são carregados usando SQLBulkImport todas as noites e nenhum dado "novo" é carregado - é um registro histórico (SQL Standard, não Enterprise, portanto, sem particionamento).

Como cada bit de dados precisa ser vinculado a outros sistemas e cada RowNumber/DataDatecombinação é única, essa é minha Chave Primária.

Percebo que, devido à maneira como defini o PK no SSMS Table Designer, ele RowNumberé listado primeiro e DataDatesegundo.

Percebo também que minha fragmentação é sempre MUITO alta ~ 99%.

Agora, como cada DataDateuma aparece apenas uma vez, eu esperaria que o indexador fosse adicionado apenas às páginas todos os dias, mas me pergunto se ele realmente é indexado com base RowNumberprimeiro e, portanto, tendo que mudar todo o resto.


Rownumbernão é uma coluna de identidade, é um int gerado por um sistema externo (infelizmente). É redefinido no início de cada um DataDate.

Dados de exemplo

RowNumber | DataDate | a | b | c..... 
   1      |2013-08-01| x | y | z 
   2      |2013-08-01| x | y | z 
...
   1      |2013-08-02| x | y | z 
   2      |2013-08-02| x | y | z 
...

Os dados estão sendo carregados em RowNumberordem, um DataDatepor carregamento.

O processo de importação é bcp - tentei carregar em uma tabela temporária e, em seguida, selecionei em ordem a partir de lá ( ORDER BY RowNumber, DataDate), mas ainda existe uma alta fragmentação.

BlueChippy
fonte

Respostas:

50

A ordem das colunas em um índice PK importa?

Sim.

Por padrão, a restrição de chave primária é imposta no SQL Server por um índice clusterizado exclusivo. O índice clusterizado define a ordem lógica das linhas na tabela. Pode haver várias páginas de índice extras adicionadas para representar os níveis superiores do índice da árvore b, mas o nível mais baixo (folha) de um índice em cluster é simplesmente a ordem lógica dos dados.

Para ser claro, as linhas em uma página não são necessariamente fisicamente armazenadas em ordem de chave de índice em cluster. Há uma estrutura de indireção separada dentro da página que armazena um ponteiro para cada linha. Essa estrutura é classificada pelas chaves de índice em cluster. Além disso, cada página possui um ponteiro para a página anterior e a próxima no mesmo nível na ordem das chaves de índice em cluster.

Com uma chave primária em cluster de (RowNumber, DataDate), as linhas são classificadas logicamente primeiro por RowNumbere depois por DataDate- para que todas as linhas RowNumber = 1sejam agrupadas logicamente, depois linhas onde RowNumber = 2e assim por diante.

Quando você adiciona novos dados ( RowNumbersde 1 a n), as novas linhas pertencem logicamente às páginas existentes; portanto, o SQL Server provavelmente precisará trabalhar muito dividindo as páginas para liberar espaço. Toda essa atividade gera muito trabalho extra (incluindo o registro das alterações) sem nenhum ganho.

As páginas divididas também começam cerca de 50% vazias; portanto, a divisão excessiva pode resultar em baixa densidade de páginas (menos linhas do que o ideal por página). Não são apenas essas más notícias para leitura do disco (densidade mais baixa = mais páginas para ler), as páginas de densidade mais baixa também ocupam mais espaço na memória quando armazenadas em cache.

Alterar o índice em cluster para (DataDate, RowNumber) significa que novos dados (presumivelmente, mais altos DataDatesque os armazenados atualmente) são anexados ao final lógico do índice em cluster em novas páginas. Isso removerá as despesas desnecessárias da divisão de páginas e resultará em tempos de carregamento mais rápidos. Dados menos fragmentados também significam que a atividade de leitura antecipada (ler páginas do disco antes de serem necessárias para uma consulta em andamento) pode ser mais eficiente.

Se nada mais, suas consultas têm muito mais probabilidade de pesquisar do DataDateque RowNumber. Um índice agrupado ativado (DataDate, RowNumber) suporta pesquisas de índice ativadas DataDate(e depois RowNumber). O arranjo existente apenas apóia as buscas RowNumber(e somente então, talvez DataDate). Você poderá soltar o índice não clusterizado existente DataDatedepois que a chave primária for alterada. O índice clusterizado será mais amplo que o índice não clusterizado que ele substitui; portanto, você deve testar para garantir que o desempenho permaneça aceitável.

Ao importar novos dados bcp, você poderá obter um desempenho superior se os dados no arquivo de importação forem classificados pelas chaves de índice em cluster (idealmente (DataDate, RowNumber)) e você especificar a bcpopção:

-h "ORDER(DataDate,RowNumber), TABLOCK"

Para obter o melhor desempenho de carregamento de dados, você pode tentar obter inserções minimamente registradas. Para mais informações, veja:

Paul White diz que a GoFundMonica
fonte
4
Uma excelente resposta - agora sei o que devo fazer e por quê. Eu pensava assim, mas não sabia! Obrigado.
precisa saber é o seguinte
Demorei um tempo demais para colocar o banco de dados no meu SQL Server local para teste: Antes de alterar a carga do índice, levava 45 minutos ... depois, eram necessários apenas 5 !!!
precisa saber é o seguinte
13

Sim, o pedido é crítico. Eu duvido que você alguma vez pergunte por RowNumber (por exemplo WHERE RowNumber=1). Séries cronológicas esmagadoras são consultadas por date ( WHERE DataDate BEWEEN @start AND @end) e essas consultas exigiriam uma organização em cluster por DataDate.

A fragmentação em geral é um arenque vermelho. Reduzir a fragmentação não deve ser seu objetivo aqui, mas ter uma organização adequada para suas consultas. Além disso, é bom ter uma fragmentação reduzida, mas não é um objetivo por si só. Se você possui um modelo de dados organizado adequadamente que corresponde à sua carga de trabalho (suas consultas são cobertas adequadamente) e possui medidas que mostram a fragmentação como impactante no desempenho, podemos conversar sobre isso.

Remus Rusanu
fonte
Eu também tenho um índice não agrupado (s) no DataDate, que, como você diz, costuma ser uma WHEREcláusula nas consultas.
precisa saber é o seguinte
1
Se ORDER das colunas for crítico, o impacto da ordem incorreta verá meu aumento de E / S? Meu pensamento é que ele está ordenando por RowNumber e, portanto, tendo que trabalhar muito nos índices todas as vezes, considerando que deve ser baseado em DataDate?
precisa saber é o seguinte