PostgreSQL 18 traz uuidv7() nativo e I/O assíncrono configurável via io_method
Lançado em setembro de 2025, o PostgreSQL 18 já passou de um ano em produção e trouxe duas mudanças que pedem ajuste de código e de configuração: a função uuidv7() para chaves primárias e o subsistema de I/O assíncrono controlado por io_method.

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 foi lançado em 25 de setembro de 2025, segundo as release notes oficiais do projeto. Passado mais de um ano, a versão já é candidata natural de upgrade para quem roda 16 ou 17 em produção, e duas mudanças da lista de novidades pedem atenção direta de quem escreve código de aplicação e de quem configura o servidor: a função uuidv7() nativa e o subsistema de I/O assíncrono (AIO), exposto pela variável io_method.
Nenhuma das duas é cosmética. A primeira muda como índices de chave primária se comportam sob carga de inserção. A segunda muda como o Postgres conversa com o disco.
uuidv7() substitui a extensão que você provavelmente já usa
Antes do Postgres 18, gerar UUID como chave primária significava escolher entre gen_random_uuid() (UUIDv4, totalmente aleatório) ou instalar uma extensão de terceiros como pg_uuidv7 para ter UUIDs ordenáveis por tempo. A versão 18 resolve isso nativamente:
CREATE TABLE pedidos (
id uuid PRIMARY KEY DEFAULT uuidv7(),
criado_em timestamptz NOT NULL DEFAULT now()
);A diferença entre UUIDv4 e UUIDv7 não é estética. UUIDv4 é aleatório byte a byte, então cada inserção cai em um ponto imprevisível da árvore do índice btree. Isso gera o que o pessoal de banco chama de index bloat por fragmentação: páginas espalhadas, cache de buffer trabalhando mais, write amplification maior em workloads de alta escrita. UUIDv7 embute um timestamp nos primeiros bits, então UUIDs gerados em sequência ficam próximos uns dos outros na ordenação, e as inserções tendem a cair no fim da árvore, como um bigserial se comportaria.
Em resumo: trocar gen_random_uuid() por uuidv7() numa tabela com volume alto de escrita tende a aliviar a pressão sobre o índice de chave primária, sem abrir mão da vantagem do UUID sobre inteiro sequencial, que é não vazar contagem de registros nem depender de uma única sequência centralizada.
O trade-off que ninguém avisa
UUIDv7 troca aleatoriedade por previsibilidade temporal, e isso tem um preço:
- Vazamento de timestamp: qualquer UUIDv7 revela, por construção, o momento aproximado em que foi gerado. Para IDs públicos (URLs, tokens expostos ao cliente), isso pode ser informação que você não queria entregar.
- Não é garantidamente único por ordem de chegada entre processos diferentes: a ordenação depende do relógio de cada backend↳Back-end49 conteúdosIntegração front-end com backend: 7 decisões que evitam caos entre APIs, BFF e GraphQLDev (Back & Front) · abr 2026Como criar uma FAKE API REST para testes — JSONPlaceholderDev (Back & Front) · set 2025Construindo um aplicativo de bate-papo de IA simples com Spring AI e AngularDev (Back & Front) · jul 2025Ver tudo em Dev (Back & Front) →; sob clock skew entre réplicas ou processos, a ordem pode não ser perfeitamente monotônica.
- Migração de coluna existente não é gratuita: trocar o
DEFAULTde uma tabela já populada comgen_random_uuid()não reordena as linhas antigas, então o ganho de locality só vale para os novos registros.
Para migrar uma coluna existente, o caminho é simples, mas precisa ser explícito sobre o que ele não faz:
ALTER TABLE pedidos ALTER COLUMN id SET DEFAULT uuidv7();Isso muda a geração daqui pra frente. Não reorganiza o índice retroativamente, e um REINDEX não vai misturar os UUIDs antigos com os novos de forma útil, porque o valor em si já foi gravado. Se o objetivo é reduzir fragmentação em uma tabela legada, a conversa é sobre reconstruir a tabela, não sobre trocar o default.
io_method: do syscall único ao I/O assíncrono
A segunda mudança é mais estrutural: o Postgres 18 adiciona um subsistema de I/O assíncrono (AIO), como registram as release notes, permitindo que o backend enfileire múltiplas requisições de leitura em vez de bloquear uma a uma. Isso melhora varreduras sequenciais, bitmap heap scans e operações de VACUUM.
O controle fica na variável de servidor io_method, em postgresql.conf, acompanhada de duas variáveis auxiliares: io_combine_limit e io_max_combine_limit, que controlam quanto cada operação de I/O pode agrupar.
Na prática, isso também destrava algo que ficava inerte em boa parte das instalações: os parâmetros effective_io_concurrency e maintenance_io_concurrency, usados para sinalizar ao sistema operacional que ele pode fazer leituras antecipadas (prefetch). Antes, eles só funcionavam de verdade em sistemas com suporte a fadvise(). Com AIO, passam a valer em mais plataformas, inclusive, e por isso o Postgres 18 também elevou o valor padrão de ambos de 1 para 16.
O que olhar antes de mexer
Trocar io_method não é um ajuste que se faz às cegas, porque o valor certo depende do sistema operacional e, em ambiente gerenciado, do que o provedor libera:
| Cenário | O que considerar |
|---|---|
Linux↳Linux34 conteúdosKali Linux em um Servidor VPS: como, quando e por que usar?DevSecOps · dez 2024Construindo um Windows Service ou Linux Daemon com Worker Service & .NET Core – Parte 2Dev (Back & Front) · jul 2020Criando uma WebApi utilizando .NET, Linux e VSCodeDev (Back & Front) · ago 2019Ver tudo em DevSecOps → com suporte a io_uring | Maior ganho potencial em scans e vacuum, mas depende de kernel recente e de o provedor permitir a configuração |
Linux sem io_uring ou outro SO | O Postgres cai para um modo de workers dedicados, mais conservador |
| Banco gerenciado (RDS, Cloud SQL e afins) | Verificar se o provedor expõe io_method antes de assumir que o ganho está disponível |
A mudança de io_method exige reinício do servidor, então não é algo para testar em produção sem antes validar em um ambiente equivalente. E, como qualquer troca de subsistema de I/O, o efeito real depende de padrão de acesso: workload com muita leitura sequencial grande (relatórios, VACUUM em tabelas enormes) tende a sentir mais do que um sistema OLTP de leituras pontuais por índice.
Um detalhe útil pra quem for depurar: o Postgres 18 adiciona a view pg_aios, que mostra os handles de arquivo em uso pelo I/O assíncrono, dando visibilidade direta sobre o que o AIO está fazendo em tempo real.
O que mais pesa na migração
Fora os dois destaques, a lista de incompatibilidades da própria documentação tem itens que afetam diretamente quem mantém pipeline de dados:
initdbagora habilita checksums de dados por padrão; quem precisa do comportamento antigo usa a nova flag--no-data-checksums. Isso importa porquepg_upgradeexige que os checksums do cluster de origem e destino sejam iguais.COPY FROMem CSV deixou de tratar\.como marcador de fim de arquivo (exceto nopsqllendo deSTDIN), e agora exige que\.apareça sozinho na linha. Scripts antigos de import que dependiam do comportamento anterior podem quebrar silenciosamente.pg_upgradeagora preserva as estatísticas do otimizador, o que elimina a necessidade de rodarANALYZEcompleto logo após o upgrade, um passo que historicamente deixava bancos grandes com planos ruins por horas.
Nenhum desses três é tão vistoso quanto uuidv7() ou io_method, mas são os que mais costumam gerar incidente silencioso em pipeline de ETL e em scripts de deploy que assumem comportamento de versões anteriores.
Quando não vale a pena migrar agora
Se a aplicação já usa bigserial como chave primária e não sofre com concorrência de inserção distribuída, trocar para uuidv7() só por modismo não traz ganho real: o problema que o UUIDv7 resolve (fragmentação de índice por aleatoriedade) simplesmente não existe nesse cenário. E se o ambiente é gerenciado e não expõe io_method, boa parte do ganho de I/O assíncrono anunciado nas release notes fica fora de alcance até o provedor liberar a configuração, mesmo depois do upgrade de versão.
Para quem já tem plano de upgrade para o Postgres 18, vale tratar uuidv7() como mudança de aplicação (trade-off explícito sobre vazamento de timestamp) e io_method como mudança de infraestrutura (depende de SO e de teste de carga antes de produção) — são duas decisões independentes, e nenhuma das duas é obrigatória só porque a versão trouxe os dois recursos.
Este artigo foi escrito por Bisneto Braga, colunista de back-end. Conteúdo produzido por agente de IA da redação iMasters, sob revisão editorial humana. Saiba como produzimos no expediente.
Rails 8 dispensa Redis com Solid Queue, Solid Cache e Solid Cable
As três ferramentas usam o banco relacional da aplicação para fazer o que hoje depende de Redis e Sidekiq.













