Logotipo WebingPro.
Menu
Background Image
Voltar para a página anterior

Quando o AUTO_INCREMENT não é mais suficiente

Postado por Gabriel Felipe em 06/10/2017 às 09:24

Background Image

Quando começamos um projeto novo e estamos criando o banco de dados, é quase automático criar uma coluna id ou similar, int(11) unsigned auto_increment como chave primária de cada tabela.

Mas e quando o auto_increment não for o suficiente? Como proceder?

“Ué, mas como assim auto_increment não ser o suficiente? Quando não vai ser o suficiente.”

Ok, comecemos por definir o problema antes de resolver ele. Imagina um sistema que se encaixe em um ou mais dos cenários abaixo:

  • Possui múltiplas instâncias do banco de dados rodando em servidores separados e precisa-se garantir que as chaves primárias geradas são únicas em todos os bancos.
  • Por algum motivo a aplicação precisa conhecer a chave primária de registros antes de inserir eles, e consultar o número anterior é inviável por questões de performance ou pelo fato do banco ser muito ativo e essa informação mudar muito rápido.
  • Por algum motivo, as chaves primárias não podem se repetir, mesmo em tabelas diferentes.

Obviamente, esses não são todos os cenários onde o auto_increment não seria o suficiente, mas serve como exemplo sobre como isso pode acontecer.

(Se você só quer a solução, pula para o final do texto, antes da solução final vou comentar uma possível solução usada mas ineficaz).

Uma alternativa muito usada nesses cenários é o GUID, basicamente o GUID (Globally Unique IDentifier) é um código único universal e um exemplo de GUID válido seria: 561d1cc4-c7b5-431e-94a7-e0c2ed9a8d2c. Existem várias implementações para gerar GUID’s, em quase todas as linguagens. A mais comum é a implementação da Microsoft.

“Interessante, então quando não der para usar auto_increment eu uso uma chave primária varchar com GUID?”

Não. Quer dizer, você poderia, e sim ia resolver os problemas que eu disse ali em cima, mas:

“O valor do GUID segue o layout de grupos de 8, 4, 4, 4 e 12 dígitos hexadecimais e minúsculos, separados por hífens “, Wikipedia

E se você está tendo problemas com o auto_increment, é muito provável que seu banco de dados seja grande / ativo. Você percebeu que o GUID é um varchar, aleatório e grande? Varchar, longo e desordenado, hmmm… não parece uma boa ideia para os índices.

Ao simplesmente usar GUID’s como sua chave primária você vai deixar seus inserts, updates e deletes muito mais lentos. Porque é muito mais difícil para o banco ter que consultar em que posição do índice ele tem que colocar cada linha nova, visto que não necessariamente os id’s novos serão maiores que os velhos.

Levando isso em conta, primary keys devem ser números inteiros e cada novo id deve sempre ser maior que os id’s já existentes na tabela. Inteiros são rápidos de ordenar e ao garantir que os novos ids são maiores e ocupam o final do índice você diminui o esforço do seu banco de dados. É, o auto_increment faz esse trabalho muito bem, quando pode.

Algumas pessoas tentam amenizar esse problema do GUID, ordenando seus caracteres e salvando no banco como binário, visto que eles são originalmente números hexadecimais.  Isso alivia o banco, mas gera índices de chaves primárias que são impossíveis de ler por um humano, visto que geram uma série de caracteres invisíveis quando traduzidos para ascii. Então lidar com esses id’s em binário é um inferno. Sério, a manutenção se torna muito sofrível.

Então, em um mundo ideal, teríamos um id que:

    • Seja um número inteiro
    • Tenha como ser gerado em vários servidores diferentes, de maneira não coordenada, e ainda assim ser único
    • Um id novo é sempre um inteiro maior que qualquer id anteriormente gerado
    • Ter menos de 64 bits

“WTF? Menos de 64 bits, por que isso?”

Algumas linguagens de programação não conseguem lidar com inteiros maiores que 64 bits. O que pode levar a vários problemas como não ser possível diferenciar um id de outro, o que seria bem triste. Outro motivo, é que se possível, quanto menor a chave melhor.

 

A solução

Várias gigantes já lidaram com esse problema, e cada uma a sua maneira. Tumblr, Instagram, Facebook e Twitter são só algumas das que possuem soluções próprias para gerar id’s de maneira eficiente. Mas a que eu gostei muito foi a solução do Twitter.

Twitter criou uma biblioteca para resolver esse problema, se chama snowflake, e você pode ler em detalhes e acessar o repositório aqui.

A ideia do twitter foi criar um serviço na rede interna deles que tem como único objetivo gerar id’s. O serviço roda na memória e é construído em Scala.

O id resultado é um número inteiro resultado da concatenação de:

  • timestamp (a partir de um epoch customizado com precisão de ms)
  • id individual por máquina gerando o id
  • um número sequencial que vai de 0 até 4096, evitando colisões na mesma máquina no mesmo segundo.

Ou seja a primeira parte do id sendo o timestamp com precisão de ms já garante que os novos id’s serão maiores que os antigo e o resultado é um inteiro mais ou menos ordenado e eficiente, que você deve armazenar no banco como BIGINT unsingned.

Se você quiser usar a solução do Twitter, ela me parece muito boa, mas, eu não queria ter instalar um servidor scala, configurar um serviço e tudo isso, minha aplicação não era tão grande.

Eu só queria gerar id’s únicos dentro do meu código PHP de uma maneira eficiente.

 

Outra Solução

Então eu fiz uma implementação similar a do Twitter, mas em PHP e disponibilizei no Github para vocês. Podem instalar com composer também, é uma única classe.

Funciona mais ou menos assim:

Pronto, você tem um id novo e único, em termos de performance, na minha máquina de desenvolvimento: I5 velho com 6gb de ram, eu gero +- 500 mil ids por segundo, o que para mim está ótimo.

Espero que aproveitem 🙂

Conheça o autor do post

Gabriel Felipe:
Notice: the_author_description está obsoleto desde a versão 2.8.0! Use the_author_meta('description') em seu lugar. in /home/webingpro/www/blog/wp-includes/functions.php on line 3830
Tenho foco em PHP, mas gosto de ficar de olho no que ta acontecendo no mercado. Já brinquei com: Python, Node.JS e Ruby on Rails. PHP ainda é minha paixão no entanto. Na formação, sou autodidata desde 2004. Aprendi o que eu sei com a internet e esse blog é uma tentativa de devolver um pouco disso. Eu gosto de opensource, e a maioria das coisas que você encontrar aqui vão estar licenciadas sobre beerware, ou licença da cerveja.