UUIDv4, UUIDv7, ULID e Sequências: A Escolha do ID impacta performance e escalabilidade do Banco
- #Java
- #Banco de Dados
- #Banco de dados relacional
O UUIDv4, gerado por UUID.randomUUID() no Java, é essencialmente aleatório. Em bancos que utilizam índices B-tree, isso faz com que cada novo registro seja inserido em um ponto imprevisível da árvore, reduzindo a localidade de dados, aumentando a fragmentação, provocando page splits e elevando o custo de I/O. Esse comportamento é amplamente observado em bancos relacionais populares como PostgreSQL,MariaDB, MySQL/InnoDB e SQL Server.
Além do impacto direto em performance, a aleatoriedade do UUIDv4 também traz desafios arquiteturais em ambientes distribuídos. Estratégias de sharding e particionamento por range tornam-se menos eficientes, já que os dados não crescem de forma ordenada. À medida que o volume aumenta, o balanceamento entre shards se torna menos previsível, exigindo redistribuições, reprocessamentos ou camadas adicionais de roteamento.
IDs sequenciais representam o extremo oposto desse espectro. Do ponto de vista de índices B-tree, são extremamente eficientes. As inserções ocorrem de forma ordenada, com excelente localidade de cache e praticamente nenhum page split. Leituras por range são rápidas e previsíveis, e o custo de manutenção do índice é mínimo. Em termos puramente de performance de banco de dados, é difícil competir com um identificador sequencial simples.
O problema dos IDs sequenciais não está na eficiência do índice, mas em outras dimensões do sistema. Eles são altamente previsíveis, o que pode expor informações sensíveis. Por exemplo, ao criar um usuário no início e no fim de um período, é possível inferir o volume total de cadastros naquele intervalo. Além disso, dependem de um mecanismo centralizado de geração, normalmente uma sequence no banco de dados. Embora sequences sejam eficientes e escalem bem em muitos cenários, elas introduzem coordenação global. Em workloads de alto volume, ambientes multi-região ou arquiteturas distribuídas, esse ponto de coordenação tende a se tornar um gargalo e dificulta a escalabilidade horizontal.
Esse conjunto de trade-offs levou ao surgimento de alternativas modernas que buscam equilibrar eficiência de índice, descentralização e previsibilidade arquitetural. UUIDv7 e ULID são exemplos claros dessa abordagem. Ambos incorporam um componente temporal nos bits mais significativos, gerando identificadores aproximadamente ordenados. Na prática, isso melhora significativamente o comportamento em índices B-tree, reduz fragmentação e page splits, melhora a localidade de cache e facilita estratégias de particionamento e sharding por range, sem abrir mão da geração descentralizada de IDs.
O UUIDv7 mantém o formato padrão de UUID, com 128 bits e representação canônica. Isso permite que ele funcione como um substituto direto do UUIDv4, sem quebra de contratos, APIs ou tipos de coluna no banco de dados. O ULID oferece benefícios semelhantes, mas adiciona melhor legibilidade para humanos, o que pode ser útil em logs, URLs e ferramentas operacionais. Por outro lado, o ULID não é um padrão oficial de UUID, o que pode exigir adaptações em bibliotecas, validações e integrações existentes.
Comparação entre estratégias de identificadores
Exemplo de geração de UUIDv7 em Java
O JDK ainda não oferece geração nativa de UUIDv7. Uma das bibliotecas mais utilizadas atualmente é a uuid-creator, que implementa o padrão conforme especificado.
Dependência Maven:
<dependency>
<groupId>com.github.f4b6a3</groupId>
<artifactId>uuid-creator</artifactId>
<version>5.3.7</version>
</dependency>
Código:
import com.github.f4b6a3.uuid.UuidCreator;
import java.util.UUID;
public class Example {
public static void main(String[] args) {
UUID uuidV7 = UuidCreator.getTimeOrderedEpoch();
System.out.println(uuidV7);
}
}
O identificador gerado mantém o formato UUID padrão, é aproximadamente ordenado por tempo e pode ser armazenado diretamente em colunas do tipo UUID no banco de dados.
Exemplo de geração de ULID em Java
Para ULID, uma biblioteca amplamente adotada é a ulid-creator.
Dependência Maven:
<dependency>
<groupId>com.github.f4b6a3</groupId>
<artifactId>ulid-creator</artifactId>
<version>5.2.3</version>
</dependency>
Código:
import com.github.f4b6a3.ulid.UlidCreator;
public class Example {
public static void main(String[] args) {
String ulid = UlidCreator.getUlid().toString();
System.out.println(ulid);
}
}
O ULID é lexicograficamente ordenável, inclui o timestamp nos bits mais significativos e costuma ser armazenado como CHAR(26) ou VARCHAR, dependendo do banco de dados.
Conclusão
A escolha do identificador primário não é um detalhe de implementação. Ela influencia diretamente performance de escrita e leitura, eficiência de índices, estratégias de particionamento, segurança da informação e a capacidade de escalar o sistema ao longo do tempo.
IDs sequenciais continuam sendo a melhor opção quando o critério é exclusivamente eficiência de índice. Em B-tree, oferecem inserções perfeitamente ordenadas, mínima fragmentação e excelente localidade de cache. No entanto, essa eficiência vem acompanhada de custos arquiteturais relevantes, como previsibilidade, exposição de informações sensíveis e dependência de coordenação centralizada. Esses fatores tornam IDs sequenciais menos adequados para sistemas distribuídos, multi-região ou que precisam gerar identificadores fora do banco de dados.
UUIDv4 resolve o problema da descentralização de forma simples e robusta, permitindo geração local de IDs sem coordenação. O custo dessa simplicidade aparece quando esses identificadores são persistidos e indexados. A aleatoriedade total degrada o comportamento de índices B-tree, aumenta page splits, fragmentação e pressão de I/O, além de dificultar sharding e particionamento por range.
UUIDv7 e ULID surgem como respostas diretas a esse dilema. Ao incorporarem o tempo nos bits mais significativos, preservam a geração descentralizada e produzem identificadores aproximadamente ordenados. Isso resulta em melhor comportamento de índices, menor custo operacional e maior previsibilidade para particionamento e balanceamento de dados.
Entre os dois, o UUIDv7 se destaca por manter compatibilidade total com o ecossistema UUID existente, funcionando como um substituto direto do UUIDv4 sem exigir mudanças em tipos de coluna, contratos ou APIs. O ULID, embora não seja um UUID oficial, oferece características semelhantes e ainda melhora a legibilidade humana, o que pode ser vantajoso em alguns contextos operacionais.
Não existe um identificador universalmente melhor. A escolha correta depende do contexto, do volume, da topologia e dos requisitos de segurança e escalabilidade. Ainda assim, para a maioria dos sistemas modernos que persistem dados em bancos relacionais e precisam escalar além de um único nó, UUIDv7 ou ULID representam hoje escolhas tecnicamente mais equilibradas do que UUIDv4 ou sequências puras.
Referências
- RFC 4122 – Universally Unique Identifier (UUID)
- RFC 9562 – UUID Version 7 and Version 8
- PostgreSQL Documentation, seções sobre B-tree indexes e sequences
- MySQL InnoDB Documentation, tópicos sobre clustered indexes e chaves aleatórias
- ULID Specification
- Martin Kleppmann, Designing Data-Intensive Applications



