Estamos trabalhando em um aplicativo Web, ainda não acessível aos usuários. Meu chefe percebeu que os registros recém-criados recebem um ID acima de 10.000, mesmo que tenhamos apenas menos de 100 registros na tabela. Ela assumiu que, por algum motivo, a interface da web cria mais de 100 vezes mais registros temporários do que os reais (e os exclui) e que isso pode nos levar a ficar fora do alcance alguns meses após o lançamento.
Não acho que ela esteja certa sobre a causa da inflação de identidade (a colega que pode responder a isso está de férias, então não sabemos ao certo), mas vamos supor que sim. Ela disse que odiaria usar uma coluna bigint e que gostaria que parássemos de aumentar automaticamente a coluna ID e escrevesse o código do servidor que escolhe o primeiro número inteiro "não utilizado" e o usa como ID.
Sou um estudante de ciências da computação com pouca experiência prática, desempenhando um papel de desenvolvedor júnior. Ela tem anos de experiência no gerenciamento de todos os bancos de dados de nossa organização e no design da maioria deles. Eu acho que ela está incorreta nesse caso, que um ID grande não é motivo para temer e que imitar a funcionalidade DBMS cheira a um antipadrão. Mas ainda não confio no meu julgamento.
Quais são os argumentos a favor e contra cada posição? Que coisas ruins podem acontecer se usarmos um bigint e quais são os perigos de reinventar a funcionalidade de incremento automático da roda ? Existe uma terceira solução que é melhor que uma? Quais poderiam ser as razões dela para evitar uma inflação dos valores de face do DI? Também estou interessado em ouvir sobre razões pragmáticas - talvez grandes identificações funcionem na teoria, mas causem dores de cabeça na prática?
Não é esperado que o aplicativo processe quantidades muito grandes de dados. Duvido que atingirá 10.000 registros reais nos próximos anos.
Se isso fizer alguma diferença, estamos usando o Microsoft SQL Server. O aplicativo é escrito em C # e usa Linq to SQL.
Atualizar
Obrigado, achei as respostas e comentários existentes interessantes. Mas receio que você tenha entendido mal a minha pergunta, então eles contêm o que eu queria saber.
Não estou realmente preocupado com o verdadeiro motivo das altas identificações. Se não conseguirmos encontrar por conta própria, eu poderia fazer uma pergunta diferente. O que me interessa é entender o processo de decisão neste caso. Para isso, suponha que o aplicativo esteja gravando 1000 registros por dia e excluindo 9999 deles . Tenho quase certeza de que não é esse o caso, mas é nisso que minha chefe acredita quando ela faz seu pedido. Portanto, nessas circunstâncias hipotéticas, quais seriam os prós e os contras de usar bigint ou escrever nosso próprio código que atribuirá IDs (de uma maneira que reutilize as IDs de registros já excluídos, para garantir que não haja lacunas)?
Quanto ao motivo real, suspeito fortemente que isso tenha acontecido porque uma vez escrevemos código para importar dados de outro banco de dados, como prova de conceito de que uma migração posterior pode ser realizada até certo ponto. Acho que meu colega realmente criou vários milhares de registros durante a importação e depois os excluiu. Preciso confirmar se esse foi realmente o caso, mas, se for, não há necessidade de ação.
fonte
Respostas:
Sem ver o código, é muito difícil dizer conclusivamente o que está acontecendo. Embora, provavelmente, o
IDENTITY
valor esteja sendo armazenado em cache, causando lacunas no valor após a reinicialização do SQL Server. Consulte /programming/17587094/identity-column-value-suddenly-jumps-to-1001-in-sql-server para obter boas respostas e informações sobre isso.Um
INT
campo simples pode conter valores de até 2.147.483.647. Você pode realmente iniciar o valor da identidade em -2.147.483.648, fornecendo 32 bits de valores completos. 4 bilhões de valores distintos. Duvido muito que você fique sem valores para usar. Supondo que seu aplicativo esteja consumindo 1.000 valores para cada linha real adicionada, você precisará criar quase 12.000 linhas por dia todos os dias para ficar sem IDs em 6 meses, desde que você inicie oIDENTITY
valor em 0 e esteja usando uma INT. Se você estivesse usando um BIGINT, teria que esperar 21 milhões de séculos antes de ficar sem valores se escrevesse 12.000 linhas por dia, consumindo 1.000 "valores" por linha.Dito tudo isso, se você quiser usar
BIGINT
como o tipo de dados do campo de identidade, certamente não há nada de errado nisso. Isso dará a você, para todos os efeitos, um suprimento ilimitado de valores a serem usados. A diferença de desempenho entre um INT e um BIGINT é praticamente inexistente no hardware moderno de 64 bits e é altamente preferível ao uso, por exemplo,NEWID()
para gerar GUIDs, por exemplo .Se você quiser gerenciar seus próprios valores para a coluna ID, poderá criar uma tabela de chaves e fornecer uma maneira bastante segura de fazer isso usando um dos métodos mostrados nas respostas nesta pergunta: Manipulando o acesso simultâneo a uma tabela de chaves sem bloqueios no SQL Server
A outra opção, supondo que você esteja usando o SQL Server 2012+, seria usar um
SEQUENCE
objeto para obter valores de ID para a coluna. No entanto, você precisará configurar a sequência para não armazenar em cache valores. Por exemplo:Em resposta à percepção negativa do seu chefe de números "altos", eu diria que diferença isso faz? Supondo que você use um
INT
campo, com umIDENTITY
, você poderia realmente iniciar oIDENTITY
at2147483647
e "incrementar" o valor em-1
. Isso não faria absolutamente nenhuma diferença no consumo de memória, desempenho ou espaço em disco usado, pois um número de 32 bits tem 4 bytes, não importa se é0
ou não2147483647
.0
em binário é00000000000000000000000000000000
quando armazenado em umINT
campo assinado de 32 bits .2147483647
é01111111111111111111111111111111
- ambos os números ocupam exatamente a mesma quantidade de espaço, tanto na memória quanto no disco, e ambos exigem exatamente a mesma quantidade de operações da CPU para serem processadas. É muito mais importante que o código do aplicativo seja projetado corretamente do que ficar obcecado com o número real armazenado em um campo-chave.Você perguntou sobre os prós e os contras de (a) usar uma coluna de ID de maior capacidade, como a
BIGINT
, ou (b) apresentar sua própria solução para evitar falhas de ID. Para responder a essas preocupações:BIGINT
em vez deINT
como o tipo de dados para a coluna em questão. O uso de aBIGINT
requer o dobro da quantidade de armazenamento, tanto em disco quanto em memória da própria coluna. Se a coluna for o índice de chave primária da tabela envolvida, todo e qualquer índice não clusterizado anexado à tabela também armazenará oBIGINT
valor, com o dobro do tamanho de umINT
, novamente na memória e no disco. O SQL Server armazena dados em disco em páginas de 8 KB, onde o número de "linhas" por "página" depende da "largura" de cada linha. Portanto, por exemplo, se você tiver uma tabela com 10 colunas, cada umaINT
, poderá armazenar aproximadamente 160 linhas por página. Se essas colunas em vez dissoBIGINT
colunas, você poderá armazenar apenas 80 linhas por página. Para uma tabela com um número muito grande de linhas, isso significa claramente que a E / S necessária para ler e gravar a tabela será o dobro neste exemplo para qualquer número determinado de linhas. Concedido, este é um exemplo bastante extremo - se você tivesse uma linha que consiste de um únicoINT
ouBIGINT
de coluna e uma únicaNCHAR(4000)
coluna, você ficaria (simplista) recebendo uma única linha por página, se você usou umINT
ou umBIGINT
. Nesse cenário, não faria muita diferença apreciável.Rolar o seu próprio cenário para evitar lacunas na coluna ID. Você precisaria escrever seu código de forma que a determinação do "próximo" valor de ID a ser usado não entre em conflito com outras ações que estão acontecendo na tabela. Algo
SELECT TOP(1) [ID] FROM [schema].[table]
parecido com ingênuo vem à mente. E se houver vários atores tentando gravar novas linhas na tabela simultaneamente? Dois atores poderiam facilmente obter o mesmo valor, resultando em um conflito de gravação. Para solucionar esse problema, é necessário serializar o acesso à tabela, reduzindo o desempenho. Existem muitos artigos escritos sobre esse problema; Vou deixar para o leitor fazer uma pesquisa sobre esse tópico.A conclusão aqui é: você precisa entender seus requisitos e estimar adequadamente o número de linhas e a largura da linha, juntamente com os requisitos de simultaneidade do seu aplicativo. Como sempre, depende.
fonte
bigint
você provavelmente vai agradecer a si mesmo para decidir que com antecedência ao invés de precisar adicionar este para uma tabela com bilhões de linhas.A principal tarefa a fazer é encontrar a causa raiz do motivo pelo qual o valor atual é tão alto.
A explicação mais razoável para as versões do SQL Server anteriores ao SQL2012 - supondo que você esteja falando de um banco de dados de teste - seria que houve um teste de carga seguido de uma limpeza.
A partir do SQL2012, o motivo mais provável deve-se a várias reinicializações do Mecanismo SQL (conforme explicado no primeiro link fornecido pelo Max).
Se a lacuna é causada por um cenário de teste, não há razão para me preocupar do meu ponto de vista. Mas, por segurança, eu verificaria os valores de identidade durante o uso normal do aplicativo, bem como antes e depois da reinicialização do mecanismo.
É "engraçado" que a MS afirme que ambas as alternativas (o sinalizador de rastreamento 272 ou o novo objeto SEQUENCE) podem afetar o desempenho.
Pode ser a melhor solução para usar o BIGINT em vez do INT, apenas para estar do lado seguro para cobrir os próximos "aprimoramentos" do MS ...
fonte
Rumtscho, Se você estiver criando apenas 1000 linhas por dia, há pouco a decidir - use o tipo de dados INT com um campo Identity e pronto. A matemática simples diz que se você der ao seu aplicativo um ciclo de vida de 30 anos (improvável), poderá ter 200.000 linhas por dia e ainda estar dentro do intervalo positivo de números de um tipo de dados INT.
O uso do BigInt é um exagero no seu caso, mas também pode causar problemas se o aplicativo ou os dados forem acessados via ODBC (como trazidos para o Excel ou MS Access, etc.), o Bigint não se traduz bem na maioria dos drivers ODBC para aplicativos de desktop.
Quanto aos GUIDS, além do espaço em disco extra e da E / S extra, há o enorme problema de que eles são projetados não sequenciais; portanto, se eles fazem parte de um índice classificado, você pode adivinhar que cada inserção será exigem que o índice seja utilizado. --Jim
fonte
Existe uma lacuna entre os valores usados? Ou os valores iniciais são 10.000 e a partir de então todos estão adicionando 1? Às vezes, se o número for fornecido aos clientes, o número inicial será maior que zero, digamos 1500 por exemplo, para que o cliente não perceba que o sistema é "novo".
A desvantagem de usar bigint em vez de smallint é que, como bigint usa "mais espaço em disco", ao ler um disco, você lê menos blocos de disco para cada disco. Se o espaço da sua linha for pequeno, isso pode ser uma desvantagem; caso contrário, não importa muito. Além disso, não importa muito se você não está consultando muitos recursos de uma só vez e se possui os índices adequados.
E, como dito em outra resposta, se você se preocupa com a falta de índices, não deve se preocupar, pois o smallint pode lidar a menos que tenha um negócio milionário. Inventar um mecanismo para "recuperar IDs" é caro e adiciona pontos de falha e complexidade ao software.
Saudações
fonte
Se eu fosse seu chefe, eu ficaria mais interessado nos motivos dos valores inesperadamente altos de ID ... do jeito que eu o vejo, para cada um dos dois cenários descritos:
Se os testes anteriores aumentassem os valores de identidade - seus outros comentários sobre o número esperado de registros também me levariam a sugerir um tipo de chave menor. Sinceramente, eu também consideraria se era possível redefinir a sequência e renumerar os registros existentes se o teste estivesse fora do caractere para o uso pretendido atual da tabela (a maioria consideraria esse exagero - 'depende').
Se a maioria dos registros gravados na tabela for excluída logo depois, eu estaria inclinado a considerar o uso de duas tabelas; uma tabela temporária em que os registros não são mantidos a longo prazo e outra em que apenas os registros que criaremos permanentemente são mantidos. Novamente, suas expectativas em relação ao número de registros de longo prazo sugerem o uso de um tipo menor para sua coluna-chave, e alguns registros por dia dificilmente farão com que um problema de desempenho 'mova' um registro de uma tabela para outra similar. 1. Suspeito que não seja o seu cenário, mas imagine que um site de compras possa preferir manter um Basket / BasketItem e, quando um pedido é realmente feito, os dados são movidos para o conjunto Order / OrderItem.
Para resumir; na minha opinião, os BIGINTs não devem necessariamente ser temidos, mas são francamente desnecessariamente grandes para muitos cenários. Se a tabela nunca aumentar, você nunca perceberá que houve um exagero na sua escolha de tipo ... mas quando tiver tabelas com milhões de linhas e muitas colunas FK que são BIGINT quando poderiam ter sido menores - então você pode desejar o os tipos foram selecionados de maneira mais conservadora (considere não apenas as colunas-chave, mas todas as colunas-chave da frente e todos os backups que você mantém, e assim por diante!). O espaço em disco nem sempre é barato (considere o disco SAN em locais gerenciados - ou seja, o espaço em disco é alugado).
Em essência, estou argumentando por uma consideração cuidadosa de sua seleção de tipos de dados sempre e não às vezes . Você nem sempre prevê os padrões de uso corretamente, mas acho que você tomará melhores decisões como regra, sempre assumindo que "quanto maior, melhor". Em geral, seleciono o menor tipo que pode conter a faixa de valores exigida e razoável e considerarei felizmente INT, SMALLINT e até TINYINT se achar que o valor provavelmente se encaixará nesse tipo no futuro próximo. No entanto, é improvável que os tipos menores sejam usados com colunas IDENTITY, mas podem ser usados alegremente com tabelas de pesquisa nas quais os valores-chave são definidos manualmente.
Finalmente, as tecnologias que as pessoas usam podem influenciar consideravelmente suas expectativas e respostas. Algumas ferramentas têm maior probabilidade de causar lacunas nos intervalos, por exemplo, pré-agendar intervalos de identidades por processo. Em contraste, o @DocSalvager sugere uma sequência auditável completa que parece refletir o ponto de vista do seu chefe; Pessoalmente, nunca exigi esse nível de autoridade - embora a regra geral de que as identidades sejam seqüenciais e geralmente sem lacunas tenha sido incrivelmente útil para situações de suporte e análise de problemas.
fonte
Usando
bigint
como uma identidade e vivendo com as lacunas:int
ainda forneceria dados de cerca de 2 milhões de dias; mais páginas terão que ser lidas e escritas; índices podem se tornar mais profundos. (Nesses volumes, isso não é uma preocupação significativa).Role o seu:
fonte
Se você está realmente preocupado em atingir o limite superior de INT para suas PKs, considere usar GUIDs. Sim, eu sei que são 16 bytes vs 4 bytes, mas o disco é barato.
Aqui está uma boa descrição de prós e contras.
fonte
Chaves primárias do RDBMS (coluna geralmente denominada 'ID') As
lacunas não podem ser evitadas nas colunas (campos) de aumento automático do RDBMS. Eles são destinados principalmente à criação de PKs exclusivas. Para desempenho, os principais produtos os alocam em lotes, de modo que os mecanismos de recuperação automática para várias falhas de operação normal podem resultar em números não utilizados. Isto é normal.
Sequências Ininterruptas
Quando você precisa de um número de sequência ininterrupto, como é normalmente esperado pelos usuários, deve ser uma coluna separada atribuída programaticamente e não deve ser a PK. Portanto, todos esses 1000 registros podem ter o mesmo número nessa coluna.
Por que os usuários desejam sequências ininterruptas?
Os números de sequência ausentes são o sinal mais básico de erro descoberto em qualquer tipo de auditoria. Este princípio da "contabilidade-101" é onipresente. No entanto, o que funciona para um pequeno número de registros mantidos manualmente, tem um problema sério quando aplicado a um número muito grande de registros nos bancos de dados ...
Reutilização de valores-chave para registros não relacionados invalida o banco de dados O
uso do "primeiro número inteiro não utilizado" introduz a probabilidade de que, em algum momento no futuro, um número seja reutilizado para registros não relacionados ao original. Isso torna o banco de dados não confiável como uma representação precisa dos fatos. Essa é a principal razão pela qual os mecanismos de incremento automático são projetados propositadamente para nunca reutilizar um valor.
fonte