Estamos pensando em usar uma sequência compartilhada para atribuir ids a chaves primárias para todas as tabelas em nosso banco de dados. Existem cerca de 100 deles. Apenas alguns são inseridos com frequência e regularidade. Queremos descartar que seja "uma ideia terrível por um motivo óbvio" antes de passarmos para a fase de realmente experimentá-lo e testá-lo em carga.
Nosso pico de carga é da ordem de 1.000 inserções por segundo, em algumas tabelas.
Nossa pesquisa até agora indica que - a velocidade de geração de sequência não deve ser um problema - a fragmentação de sequência (lacunas) acontecerá, mas não deve ser um problema - a exaustão de id não será um problema
Não temos certeza se estamos perdendo outras grandes coisas. Ficaríamos gratos pelas opiniões das pessoas, especialmente de pessoas que tentaram antes e tiveram experiências positivas ou negativas.
Por contexto - temos duas motivações principais para fazer isso.
Uma motivação para fazer isso é para que possamos definir um monte de dicionários (nós os chamamos de escopos) e ter palavras legíveis para humanos atribuídas a esses ids, então queremos ter certeza de que ids em tabelas diferentes nunca se sobreponham. Assim, em um escopo, id 12345 pode receber o valor "Green" e em outro pode ser atribuído "Verde". (Na verdade, não o usamos para internacionalização, mas podemos um dia).
A outra motivação é tornar mais fácil ter várias implantações no campo e saber (definindo exclusivamente a sequência de cada par de dígitos mais significativos) que nossas implantações não sobreporão as chaves primárias. (Como um GUID lite).
Três possíveis questões que vêm à mente são:
Com qualquer recurso compartilhado, você está criando um gargalo em potencial. Meu instinto diz que, para o seu pico de carga, isso não deve ser um problema, mas sugiro fortemente comparar qualquer solução desse tipo em um ambiente de produção semelhante à produção para ter certeza.
Você está essencialmente atribuindo significado a chaves substitutas que anulam parte de seu propósito na teoria RDB. Uma chave substituta por sua natureza não deve ter significado além de ser uma chave para identificar tuplas nessa relação. Se as entidades podem ter significado juntas e, portanto, precisam de chaves livres de colisão, é correto que elas estejam sendo modeladas separadamente ou algo foi perdido nos requisitos e/ou no design do modelo de dados?
Você está introduzindo um ponto potencial de falha. E se uma implantação não obtiver seu ponto inicial de sequência inicial definido? Você então tem um erro de bloqueio de implantação ou as implantações começam no mesmo local "quebrando" seu recurso. Além disso, o que você fará se em algum lugar no futuro alguém achar que é uma boa ideia ramificar uma implantação (em produção, talvez uma empresa inquilina aliene parte de si mesma e precise separar os dados). E se a semente de alguma forma for redefinida por uma implantação de atualização incorreta ou outra migração? [0]
Se nenhum desses problemas o preocupa, vá em frente, a ideia não vai quebrar nada IMO. Claro que pode haver maneiras melhores , mesmo que esta não esteja errada em si.
Quando você diz "UUID-lite" significa que já considerou e descontou os UUIDs. É esse o caso e, em caso afirmativo, existem razões específicas para decidir que não são adequados para este projeto?
Uma possível razão para não usar UUIDs é a fragmentação do índice, embora o significado disso seja muitas vezes superestimado [1] . A resposta do SQL Server para isso é o "GUID sequencial", que é praticamente equivalente ao que você está sugerindo se descontarmos a atribuição de significado aos valores-chave - talvez o postgres tenha um equivalente a isso? É claro que índices sempre crescentes podem ter seus próprios problemas de desempenho (contenção de última página, estatísticas de índice ficando obsoletas) em algumas cargas de trabalho de alto volume muito específicas [2] .
Outro argumento comum contra UUIDs é o tamanho da chave: por que usar 16 bytes por valor quando 4 ou 8 serão suficientes? Se a exclusividade for realmente uma propriedade útil, isso geralmente superará significativamente as preocupações com o tamanho da chave. Se o tamanho da chave é uma preocupação, mas você está feliz em usar um INT de 64 bits em vez de precisar manter dentro de 32 bits, você pode usar sua técnica sem adicionar um possível problema de contenção de recursos compartilhados, fazendo sua ideia de chave inteira semeada por tabela [3] usando uma definição de coluna
INT IDENTITY(<start>, 1)
[4] normal , embora novamente isso esteja adicionando complexidade de implantação (uma pequena quantidade, mas certamente não zero).A legibilidade humana às vezes é citada como um problema, mas isso remonta a atribuir significado a chaves substitutas.
A compressibilidade é uma preocupação menos comum, mas que você pode encontrar. Para praticamente qualquer algoritmo de compactação, os UUIDs provavelmente se parecerão com dados aleatórios (portanto, não compactáveis), a menos que você esteja usando algo como UUIDs sequenciais do SQL Server. Isso pode ser uma preocupação para um conjunto muito grande de links (ou outro bloco de dados) que contém muitos IDs de entidade servidos a um aplicativo em uma rede lenta ou se precisar usar algo como os recursos de compactação de índice do SQL Server, embora ambos sejam importantes estão essencialmente apenas reafirmando a preocupação com o tamanho da chave de uma maneira ligeiramente diferente e os UUIDs sequenciais também podem ajudar aqui.
[0] isso também pode acontecer para colunas de identidade normais, é claro, mas como você está usando um recurso menos comum, está aumentando a chance de um DBA menos experiente depois de perder o problema, se isso acontecer quando você estiver fazendo algo novo e empolgante em outro lugar!
[1] Sou um cara do SQL Server, suspeito que o problema potencial seja o mesmo no postgres, mas pelo que sei, pode ter um layout de índice diferente que pode atenuar o efeito.
[2] Embora, novamente, estes possam ser específicos do SQL Server, especialmente o último dos dois exemplos que listei
[3] Dois principais bytes: variam por banco de dados, próximos dois: variam por tabela, quatro restantes: os bits de incremento
[4] Essa é a sintaxe do MS SQL Server, a sintaxe do postgres pode variar, mas você deve ver o que quero dizer e ser capaz de traduzir
tl; dr: se você estiver reinventando a roda, certifique-se de que todos os designs existentes realmente não são adequados antes de começar a considerar por que um novo pode ou não ser.
Essa é uma ideia horrível: descarte-a. Basta usar um GUID/UUID. Por que você descartou essa ideia? No PostgreSQL usamos
uuid-ossp
,Assim,
Você faz muitas suposições em sua resposta para que ela seja válida,
Você não precisa assumir nada disso. E se você obtiver um DOS no ID criando uma lacuna enorme e empurrando o rollover em um fragmento? Por que não usar a solução da indústria para esse problema? Não está claro que há uma única desvantagem. É provável que todos ganhem. Exceto por alguns bytes de armazenamento.
Eu usei o padrão que você propôs com uma tabela de ID central adicional para a qual todas as outras chaves estrangeiras de IDs. Funcionou em um grande sistema de produção totalmente bem.
Eu acho que a verdadeira razão para fazer isso é se seus ids tiverem um escopo além do seu banco de dados. Por exemplo, no meu exemplo, esses IDs enumeravam títulos e empresas financeiras exclusivas. Você pode perguntar, por que não criar um conjunto de ids para empresas e um segundo conjunto para títulos, como chaves primárias de autoincremento em cada tabela? Porque queríamos que outros registros de séries temporais se referissem a títulos ou empresas. Portanto, a tabela de séries temporais é chaveada para a tabela de id central.
Dado o exposto, um GUID / UUID também funcionaria bem. No entanto, esses formatos geralmente têm tamanho de 128 bits, o que pode ter um impacto, pois são usados em quase todos os índices, chaves primárias e chaves estrangeiras no banco de dados, e mitigar seu posicionamento não sequencial no intervalo total de id pode ser complicado, levando para um desempenho de seleção abaixo do ideal. Nosso banco de dados era muito voltado para selecionar o desempenho.
GUIDs / UUIDs têm uma vantagem, que é que eles são muito mais fáceis de criar processos de geração federados. Ou seja, você pode ter vários processos de geração / atribuição de id em sua empresa sem coordenação, apenas assumindo que eles nunca entrarão em conflito. Se seus únicos processos de geração de id estiverem dentro de seu banco de dados, isso não é uma preocupação, mas vale a pena mencionar.
Observe que a geração de UUID depende de obter seus endereços MAC exclusivos, portanto, você terá que prestar atenção a isso em um ambiente virtual/contêiner.
Por si só, eu não deixaria que isso fosse o motivo de escolher um design peculiar e frágil. Se você seguir o caminho não haverá como aproveitar os recursos do banco de dados para garantir a integridade referencial, por exemplo. Uma maneira normalizada tradicional de alcançar a mesma coisa teria benefícios além do RI:
dbfiddle aqui
Eu sugeriria, como outros fizeram, que usar UUID é muito melhor (ou seja, muito menos propenso a erros) do que inventar um novo UUID-lite.
No entanto, ainda não acho que seja sua melhor aposta - você não está fragmentando, portanto, não há necessidade real de ter IDs não sobrepostos entre implantações que posso ver nas informações fornecidas. Presumivelmente, você tem outras maneiras de identificar uma implantação em um banco de dados além de examinar os IDs nessas tabelas.