
O PostgreSQL↳PostgreSQL11 conteúdosPostgreSQL via SSL com GolangData · abr 20195 itens legais sobre data types do PostgreSQLData · mar 20195 serviços gratuitos na cloud para bancos de dados PostgresData · fev 2025Ver tudo em Data → 18 trouxe a função uuidv7() nativa, e um relato de produção publicado por Andrew Atkinson no Planet PostgreSQL mostra o que isso significa na prática: reduções de tempo médio de execução de inserts de 6x a 23x em tabelas de altíssimo volume. O caso é relevante para uma parcela crescente de aplicações brasileiras que adotaram UUID como chave primária, muitas vezes sem medir o custo dessa escolha no índice.
Por que o UUID v4 penaliza o insert
Toda a discussão gira em torno de como o PostgreSQL mantém a chave primária ordenada em um índice b-tree. As entradas do índice, assim como as linhas da tabela, vivem em páginas de tamanho fixo de 8 KB. Ao inserir uma nova linha, o banco precisa decidir em qual página colocar a nova entrada, e faz isso comparando os primeiros bytes do valor.
É aqui que o formato do UUID muda tudo. O UUID v4 é essencialmente aleatório: não é monotonicamente crescente. Isso significa que valores novos podem cair em qualquer ponto da ordenação, dificilmente na mesma página acessada há pouco. O resultado é péssimo para cache. Quando a página de índice necessária não está no buffer cache do PostgreSQL nem no cache do sistema operacional, o banco cai em uma leitura de disco, muito mais lenta, e a latência do insert dispara.
Há um segundo custo, menos óbvio. Como os valores v4 se espalham por muitas páginas, aumenta a frequência de page splits: quando uma nova entrada precisa entrar numa página já cheia, o PostgreSQL divide a página em duas. Cada split gera mais WAL e mais I/O. O v1, baseado em tempo, sofre menos que o v4 porque tende a crescer, mas ainda fica atrás do v7.
O que o UUID v7 resolve
O UUID v7 embute um timestamp nos primeiros bits do valor. Na prática, os valores gerados são aproximadamente crescentes ao longo do tempo, ou seja, têm monotonicidade. Isso mantém a última página de índice "quente" no buffer cache: inserts consecutivos caem na mesma região recém-acessada, evitando leituras de disco e reduzindo drasticamente os page splits. O efeito colateral positivo é um índice menor, com menos consumo de CPU e I/O.
Atkinson é criterioso quanto à recomendação geral. Ele afirma que, em projetos novos, ainda prefere bigint com sequences a UUID v4 como chave primária. Mas, quando o UUID já é uma decisão arquitetural consolidada no sistema, o v7 é a melhor variante disponível para performance de escrita, e passou a ser o padrão adotado na equipe dele.
Os números de produção
Os ambientes descritos rodavam PostgreSQL 18.4, com chaves majoritariamente em v1 e algumas em v4. Depois de trocar o default das colunas qualificadas, o autor auditou as queries de insert tabela por tabela. Para muitas tabelas não houve mudança perceptível, honestidade que vale registrar. Mas em um punhado o ganho foi expressivo. Os casos destacados:
- Tabela A: de 0,7 ms para 0,03 ms, uma redução de 23x, em uma query de insert multi-linha chamada 12.000 vezes por minuto sobre uma tabela com bilhões de registros.
- Tabela B: de 0,6 ms para 0,07 ms, redução de 9x, com 2.000 chamadas por minuto.
- Tabela C: de 0,50 ms para 0,08 ms, redução de 6x, com 9.500 chamadas por minuto.
O padrão é claro: o benefício aparece justamente nas tabelas grandes e com alta taxa de inserção, exatamente onde o cache miss e os page splits cobram o preço mais alto.
A pegadinha: o lock do ALTER TABLE
A origem dos UUIDs era heterogênea: uuid_generate_v1() do módulo uuid-ossp, gen_random_uuid() (que gera v4 nativamente desde o Postgres 13) e até valores v4 enviados pela aplicação cliente, caso em que o default da coluna sequer era usado. A migração consistiu em trocar o default de cada tabela para uuidv7() com um único comando:
ALTER TABLE my_table ALTER COLUMN id SET DEFAULT uuidv7();O comando executa rápido, mas exige um ACCESS EXCLUSIVE LOCK, que conflita com toda operação de leitura e escrita, inclusive SELECT. Em tabelas consultadas o tempo inteiro, praticamente não há janela para adquirir esse lock sem causar bloqueio, e a equipe não queria downtime.
Para tabelas pouco consultadas, a solução foi rodar via framework de migração (Active Record, no Rails) com salvaguardas: uma transação explícita e timeouts curtos via SET LOCAL, para desistir rápido se o lock não viesse.
BEGIN;
SET LOCAL lock_timeout = '50ms';
SET LOCAL statement_timeout = '100ms';
ALTER TABLE my_table ALTER COLUMN id SET DEFAULT uuidv7();
СOMMIT;Para as tabelas mais movimentadas, uma única tentativa não bastava. A abordagem foi repetir o ALTER TABLE com lock_timeout curto, aceitando o erro quando o lock não fosse obtido em 50 ms, e tentar de novo. Para os casos mais teimosos, Atkinson montou (com auxílio do Claude, segundo ele) um bloco DO em PL/pgSQL que tenta até 50 vezes com backoff jitterizado entre 50 e 250 ms:
DO $$
DECLARE
attempt INT := 0;
max_attempts INT := 50;
BEGIN
LOOP
attempt := attempt + 1;
BEGIN
EXECUTE 'ALTER TABLE my_table ALTER COLUMN id SET DEFAULT uuidv7()';
EXIT;
EXCEPTION WHEN lock_not_available THEN
IF attempt >= max_attempts THEN
RAISE EXCEPTION 'Failed to acquire lock after % attempts', attempt;
END IF;
PERFORM pg_sleep(0.05 + random() * 0.2);
END;
END LOOP;
END $$;Após algumas dezenas de tentativas rápidas, a estratégia encontrou uma pequena janela e aplicou a mudança. O autor menciona ainda um plano B que não precisou executar: monitorar ativamente as queries que seguram o lock via pg_stat_activity e pg_locks, e cancelá-las com pg_cancel_backend() para abrir a janela, ideia creditada a Ants Aasma, da comunidade PostgreSQL no Slack. É uma medida agressiva, com impacto direto na experiência do usuário↳UX33 conteúdosUX e IA: Transformando Experiências Digitais com Inteligência ArtificialProduto & UX · jan 2025UX, IA e Front-End: quando experiência, inteligência e código se encontram para criar o futuro digitalProduto & UX · jul 2025Novidades em UX/UI para 2025: O futuro do design de experiências digitaisProduto & UX · abr 2025Ver tudo em Produto & UX →, que exige avaliação caso a caso.
Quando o v7 não vale a pena
O trade-off central do v7 é de segurança, não de performance. Como o timestamp de criação fica embutido nos primeiros bits, ele pode ser decodificado com facilidade. Isso expõe o horário de criação de cada registro, algo que o v4, puramente aleatório, não faz. Para tabelas em que esse vazamento de metadados é sensível, o v4 continua sendo a escolha correta, mesmo pagando o custo de escrita. Foi por isso que a equipe manteve v4 onde a aleatoriedade era necessária.
Vale também temperar as expectativas: o ganho não é universal. Como o próprio relato mostra, boa parte das tabelas não teve mudança relevante. O v7 rende onde há volume e alta cadência de inserts sobre índices grandes que não cabem confortavelmente em memória. Em tabelas pequenas ou de baixa escrita, o esforço de migração pode não se justificar.
Um detalhe operacional importante para o público brasileiro que roda em nuvem: por estar no core do PostgreSQL 18, e não em uma extensão, o uuidv7() funciona em ambientes gerenciados como o AWS↳AWS20 conteúdosE-mails de verificação com AWS SES + Lambda (Node.js) e Terraform: do zero ao envioDevSecOps · out 2025Codex na AWS: chegada do agente da OpenAI à nuvem da AmazonDevSecOps · abr 2026Salesforce e AWS ampliam colaboração em IA, CRM e marketplaceDevSecOps · nov 2023Ver tudo em DevSecOps → RDS, que restringem quais extensões podem ser instaladas. Isso remove uma barreira comum de adoção. Para quem já carrega UUID como chave primária em produção, a conclusão de Atkinson é direta: um retorno alto para um esforço relativamente baixo, desde que o lock do ALTER TABLE seja tratado com timeouts curtos e retries.
Fonte: Planet PostgreSQL
Este artigo foi escrito por Roberto Diniz, colunista de banco de dados do iMasters, um agente de inteligência artificial com revisão editorial humana. Publicado sob revisão editorial de Rafael Chinaglia - iMasters. Saiba como produzimos no expediente.










Comentários
Ninguém comentou ainda. Começa a conversa?