21 pontos por GN⁺ 2024-07-06 | 4 comentários | Compartilhar no WhatsApp
  • UUID é frequentemente usado como chave primária de tabelas de banco de dados
    • É fácil de gerar, fácil de compartilhar entre sistemas distribuídos e garante unicidade
    • Considerando o tamanho do UUID, pode surgir a dúvida se essa é a escolha certa, mas muitas vezes não temos como decidir isso
  • Este texto não foca em "UUID é um formato adequado para chave?", e sim em como usar UUID com eficiência como chave primária no PostgreSQL

Usando PostgreSQL e UUID como chave primária

  • O que é UUID?
    • UUID é frequentemente usado como chave primária de tabelas de banco de dados
    • Pode ser compartilhado facilmente entre sistemas distribuídos e garante unicidade
    • Por causa do tamanho do UUID, pode haver dúvidas sobre sua adequação, mas muitas vezes não há alternativa de escolha

Tipo de dado UUID no PostgreSQL

  • Armazenar UUID como string

    • O PostgreSQL oferece o tipo de dado text para armazenar strings
    • Porém, o tipo text não é adequado para armazenar UUID
    • O PostgreSQL oferece o tipo de dado dedicado uuid para UUID
    • O tipo uuid é um tipo de dado de 128 bits, e requer 16 bytes para armazenar um valor
    • O tipo text adiciona um overhead de 1 ou 4 bytes
  • Resultados do experimento

    • Foram criadas duas tabelas para comparação: uma com tipo text e outra com tipo uuid
    • Após inserir 10.000.000 de linhas, foram comparados o tamanho da tabela e o tamanho do índice
    • A tabela que usa o tipo text ficou 54% maior, e o tamanho do índice ficou 85% maior

UUID e índices B-Tree

  • Índices B-Tree e UUID

    • UUID aleatório não é adequado para índices B-Tree
    • Índices B-Tree funcionam bem com valores ordenados
    • UUID.randomUUID() do Java retorna UUID v4, que é um valor pseudoaleatório
    • UUID v7 gera valores ordenados no tempo, sendo mais adequado para índices B-Tree
  • Uso de UUID v7

    • Para usar UUID v7 em Java, é necessária a biblioteca java-uuid-generator
    • Gerar UUID v7 pode melhorar o desempenho de inserção

Impacto do UUID v7 no desempenho de INSERT

  • Experimento
    • Foi criada uma tabela usando UUID v7, e o desempenho foi medido inserindo 10.000 linhas 10 vezes
    • Os resultados são um tanto aleatórios, mas inserir UUID v7 é cerca de 2 vezes mais rápido

Leitura adicional

  • Há possibilidade de o PostgreSQL 17 oferecer suporte nativo a UUID v7
  • Informações sobre o formato UUID v7
  • Impacto do UUID no desempenho como chave primária de banco de dados

Resumo

  • Problema do tamanho do UUID

    • Mesmo com otimizações, UUID não é o tipo ideal como chave primária
    • Se houver liberdade de escolha, vale considerar outras opções, como TSID
  • Necessidade de otimização

    • Se forem esperados grandes volumes de dados ou alto tráfego, é preciso considerar otimização
    • Alterar a chave primária é uma tarefa difícil, então é importante definir isso corretamente desde o início
  • Observações

    • O autor não é especialista em PostgreSQL, e está apenas compartilhando o que aprendeu
    • Se isso foi útil, ele pede feedback por comentários ou pelo Twitter

Resumo do GN⁺

  • Este texto trata de maneiras eficientes de usar UUID como chave primária no PostgreSQL
  • Mostra por meio de experimento que usar UUID v7 pode melhorar o desempenho de inserção
  • Se forem esperados grandes volumes de dados ou alto tráfego, é necessário considerar otimização
  • Também vale considerar outras opções, como TSID

4 comentários

 
savvykang 2024-07-09

Será que é pedir demais querer codificação em base62 para UUID, em vez do formato padrão (hexadecimal + hífens)?

 
qurare 2024-07-08

uuidv7 é imbatível

uuidv8+ é um "deus"

 
bbulbum 2024-07-08

O maior obstáculo é que não é amigável para humanos... Eu ainda sinto falta disso em muitos aspectos...

 
GN⁺ 2024-07-06
Comentários do Hacker News
  • Recomenda-se usar bigserial como chave primária amigável a B-tree e considerar UUID codificado como string como opção de localizador de registro externo

    • Se usuários não técnicos precisarem citar isso, vale considerar primeiro uma opção simples, como um localizador no estilo PNR
    • Não misture tipos de PK dentro do esquema de um serviço ou aplicação
    • Ao usar UUIDv7 como identificador único, use-o apenas para dados que tenham um timecode embutido
    • Não use hashids; não tem qualidade criptográfica e não é familiar para pessoas comuns
    • Ao codificar, não use base64 nem alfabetos que incluam hífens
  • Ao projetar o esquema de um banco de dados, tenha em mente os princípios de separação de interesses e afinidade mecânica

  • Os IDs aleatórios tipados da Stripe na verdade não são aleatórios

    • Incluem metadados, timestamps embutidos, shard e chaves de referência, informações de versão etc.
    • Pessoalmente, prefiro localizadores bigserial+HMAC criptografados com AES e codificados em base58
  • No Postgres, UUIDs aleatórios não são um grande problema

    • UUID (16 bytes) é maior que serial (4 bytes) ou bigserial (8 bytes), mas isso não é um grande problema no nível da tabela inteira
  • Antes de considerar serial vs. UUID aleatório vs. UUID ordenado no Postgres, há muitas outras coisas com que se preocupar

  • Recentemente escolhi ULID como PK no Postgres e este artigo ajudou bastante: https://brandur.org/nanoglyphs/026-ids

  • A preferência por ULID vem do fato de ele ser compatível com o tipo UUID e ter timestamp embutido, então ao ordenar pelos IDs eles ficam em ordem de timestamp

  • Seria bom incluir int64 na comparação para avaliar a sobrecarga de UUIDs em relação à abordagem tradicional

  • Desempenho de inserção é uma forma ruim de avaliar desempenho

    • O desempenho de B-Tree é melhor nas inserções, mas fica a dúvida de como isso se comporta em transações de grande escala
  • No SQLite, UUID4 é preferido porque há menos chance de colisão no cache de páginas durante o bloqueio de transações

    • Isso também pode se aplicar de forma semelhante em sistemas Postgres
  • Prefiro chaves primárias inteiras com incremento automático

    • São fáceis de entender e simples de ordenar
    • Em projetos grandes em lote, é possível armazenar a última chave primária e buscar tudo o que for maior que ela
  • O benchmark de tempo de inserção do UUIDv7 inclui o tempo de geração do UUID

    • Eu gostaria de ver apenas o custo de atualização do índice isoladamente
  • É improvável que o PostgreSQL 17 inclua suporte a UUIDv7

    • O committer foi removido do trabalho recente, e a versão 17 já está em congelamento de funcionalidades
  • Comecei a usar python-ulid, e ULID é superior ao UUID

  • Como o link do padrão UUID v7 está desatualizado, consulte a RFC 9562: https://datatracker.ietf.org/doc/html/rfc9562