GUID sequencial ou bigint para PK de tabela de banco de dados 'enorme'


14

Sei que esse tipo de pergunta surge muito, mas ainda não li argumentos convincentes para me ajudar a tomar essa decisão. Por favor, tenha paciência comigo!

Eu tenho um banco de dados enorme - cresce cerca de 10.000.000 de registros por dia. Os dados são relacionais e, por razões de desempenho, carrego a tabela com BULK COPY. Por esse motivo, preciso gerar chaves para as linhas e não posso confiar em uma coluna IDENTITY.

Um número inteiro de 64 bits - um bigint - é amplo o suficiente para eu usar, mas para garantir a exclusividade, preciso de um gerador centralizado para fazer meus IDs para mim. Atualmente, tenho um serviço desse gerador que permite que um serviço reserve números de sequência X e garanta que não haja colisões. No entanto, uma conseqüência disso é que todos os serviços que eu tenho dependem desse gerador centralizado e, portanto, sou limitado em como posso distribuir meu sistema e não estou satisfeito com as outras dependências (como exigir acesso à rede) impostas por este design. Este tem sido um problema na ocasião.

Agora estou pensando em usar GUIDs seqüenciais como minhas chaves primárias (geradas externamente para SQL). Tanto quanto pude verificar com meus próprios testes, a única desvantagem é a sobrecarga de espaço em disco de um tipo de dados mais amplo (que é exacerbado pelo uso em índices). Eu não testemunhei nenhuma desaceleração discernível no desempenho da consulta, em comparação com a alternativa bigint. Carregar a mesa com BULK COPY é um pouco mais lento, mas não muito. Meus índices baseados em GUID não estão se fragmentando graças à minha implementação sequencial de GUID.

Basicamente, o que eu quero saber é se existem outras considerações que eu possa ter esquecido. No momento, estou inclinado a dar o salto e começar a usar GUIDs. Como não sou especialista em banco de dados, gostaria muito de receber qualquer orientação.


2
Como você geraria um "GUID sequencial"?

É uma implementação personalizada. É basicamente um formato do tipo GUID que substitui 6 bytes por bytes de carimbo de data e hora e 2 bytes que representam um número de sequência em que o carimbo de data e hora é o mesmo. Não é garantido que produza valores sequenciais perfeitos, mas é bom o suficiente para tornar a fragmentação do índice um problema para mim.

Você está, portanto, carregando esses dados de várias fontes diferentes? Também estou assumindo que o índice que você está preocupado em fragmentar é o índice em cluster?

2
Se você estiver usando um GUID seqüencial, consulte NEWSEQUENTIALID (). Ele deve fazer o que você deseja (aumentando monotonicamente) e não depende de código personalizado.

2
Veja a postagem de Jeremiah Peschka em O problema com as chaves Boa leitura e ele lida com essas implementações várias vezes.
billinkc

Respostas:


4

Estou em uma mesma situação semelhante. Atualmente, estou usando a abordagem GUID seqüencial e não tenho fragmentação nem geração fácil de chaves.

Percebi duas desvantagens que me fizeram começar a migrar para o bigint:

  1. Uso de espaço . 8 bytes a mais por índice. Multiplique isso por 10 índices ou mais e você terá um enorme desperdício de espaço.
  2. Os índices columnstore não suportam GUIDs.

(2) Foi o assassino para mim.

Agora vou gerar minhas chaves assim:

yyMMddHH1234567890

Eu usarei uma data inicial mais uma hora e ter uma parte seqüencial depois disso. Isso me permite consultar meus dados por data sem nenhum índice de adição. Este é um bom bônus para mim.

Gerarei a parte seqüencial do bigint usando um algoritmo HiLo que se presta bem à distribuição .

Espero que parte disso seja transferida para a sua situação. Definitivamente, recomendo usar bigint.


1
Marcando isso como a 'resposta', como é o melhor ajuste (e você parece apreciar o que estou perguntando e por que isso não é tão simples como pode parecer à primeira vista). Eu acho que vou usar um gerador de sequência compartilhada (que funcionará de maneira semelhante à sua sugestão de algoritmo HiLo). Eu tenho isso trabalhando em outro sistema com poucos problemas, só vou ter que aturar a dependência extra. Ah bem. Obrigado.
Barguast

3

Com um tipo INT, começando em 1, você obtém mais de 2 bilhões de linhas possíveis - isso deve ser mais do que suficiente para a grande maioria dos casos. Com BIGINT, você recebe aproximadamente 922 quatrilhões (922 com 15 zeros - 922'000 bilhões) - o suficiente para você?

Se você usar uma INT IDENTITYpartida em 1 e inserir uma linha a cada segundo, precisará de 66,5 anos antes de atingir o limite de 2 bilhões.

Se você usar a BIGINT IDENTITYpartir de 1 e inserir mil linhas por segundo, precisará de 292 milhões de anos antes de atingir o limite de 922 quatrilhões ...

Usando seus 10 milhões de linhas por dia, você terá números suficientes para aproximadamente 1'844'674'407'370 dias ( 1844 bilhões de dias ou mais de 5 bilhões de anos ) de dados - isso é suficiente para suas necessidades ?

Leia mais sobre isso (com todas as opções disponíveis) nos Manuais Online do MSDN .


1
A taxa de inserção de 10 milhões de linhas por dia esgotaria o intervalo INT em 200 dias.
Mceda

@ Mceda: Sim - eu reivindiquei mais alguma coisa? Mas não esgota o BIGINTalcance tão rapidamente ...
marc_s 28/12

Obrigado, mas como eu disse na minha pergunta, preciso dos IDs antes de serem enviados ao banco de dados. Os dados são relacionais, portanto, preciso atribuir chaves primárias e estrangeiras antes de serem copiadas em massa. Se não fosse por isso, uma IDENTITY BIGINT provavelmente seria perfeita.

2
@ Barguast: você não poderia simplesmente inserir seus dados em massa em uma tabela intermediária (sem a identidade) e depois movê-los para suas tabelas de dados reais usando BIGINT IDENTITY?
Marc

@marc_s: sim, o cálculo fornecido não estava alinhado com a pergunta: "Se você usa uma INT IDENTITY a partir de 1 e insere uma linha a cada segundo, precisa de 66,5 anos antes de atingir o limite de 2 bilhões".
Mceda

2

Eu recomendo que você use o tipo de dados SEQUENCE of BIGINT no SQL 2012. Isso é muito mais flexível que o IDENTITY com opções como cache / nocache, você também pode atribuir um intervalo de sequência para sua operação em lote como sp_sequence_get_range.


Infelizmente, SEQUENCE não é suportado no Sql Azure.
Timothy Lee Russell

2

É por isso que você não pode usar IDENTITY porque já existem relacionamentos de chave estrangeira entre tabelas separadas que você está carregando? E não há outra chave natural para você poder vinculá-las em uma operação de uma área intermediária para a área de produção? Por esse motivo, gostaria de saber um pouco mais sobre como eles estão atualmente "vinculados" no sistema de origem antes de você copiar em massa? Os sistemas de múltiplas fontes simplesmente usam suas próprias sequências e têm a possibilidade de sequências conflitantes quando trazidos para um banco de dados compartilhado?

A técnica COMB ID / GUID seqüencial é uma que eu conheço e é viável sempre que você precisar efetivamente da exclusividade global atribuída fora do banco de dados - é efetivamente uma identidade de linha utilizável dentro e fora do banco de dados. Por esse motivo, em ambientes altamente distribuídos ou em cenários desconectados, é uma boa opção

Exceto se você realmente não precisar, porque essa diferença de largura extra é significativa quando o tamanho dos dados aumenta e essas chaves estão em todos os índices e nos conjuntos de trabalho para muitas consultas.

Além disso, com a geração distribuída, se as linhas realmente não estiverem na ordem da coluna GUID, os problemas com o uso dessa chave de índice em cluster (estreita, estática, crescente) potencialmente causam alguma fragmentação em comparação com o cluster em uma IDENTITY ainda permanecer.


0

Em geral, é possível usar a OUTPUTcláusula de INSERTcomando para inserir dados nas duas tabelas e relacionados ao campo de identidade.

O identificador baseado no registro de data e hora não deve ser considerado confiável - depende do relógio do sistema, que por sua vez depende de muitas coisas - do relógio do hardware aos serviços de sincronização de horário.

Ao utilizar nosso site, você reconhece que leu e compreendeu nossa Política de Cookies e nossa Política de Privacidade.
Licensed under cc by-sa 3.0 with attribution required.