Paginação Offset vs Cursor: Performance e Melhores Práticas no SQL
As diferenças e impactos reais das estratégias Offset-based e Cursor-based Pagination para bases de dados grandes. escolher a abordagem ideal em cada cenário e melhore a performance das
Por que isso é importante
Paginação: cursor para escala — offset em feed quente vira bug de página fantasma.
Leitura relacionada: Curso Node.js · Clean Vertical Slice · Programa CrazyStack · Cursos CrazyStack.
O que é Offset-Based Pagination?
O modelo Offset-Based Pagination utiliza os comandos OFFSET e LIMIT nas queries para
retornar apenas um subconjunto de registros, permitindo navegar entre "páginas" de dados
com facilidade. Por exemplo, ao consultar a página 2 com 10 itens por página, você faz
OFFSET 10 LIMIT 10, pulando os 10 primeiros e pegando os próximos 10 resultados.
Atenção
Apesar de ser simples e bastante utilizado, o OFFSET não reduz a quantidade de
registros consultados internamente pelo banco – apenas os registros retornados para o
cliente.
Por que o OFFSET traz problemas de performance?
Quando se trabalha com tabelas que possuem milhões de registros, o banco precisa
percorrer (e processar) todos os registros até o número do OFFSET antes de retornar o
resultado solicitado. Isso faz com que consultas em páginas distantes da primeira
aumentem exponencialmente em tempo de execução e uso de memória, tornando o processo
cada vez mais lento e custoso.
Atenção
Em cenários de paginação profunda (ex: OFFSET 1.000.000), o banco pode demorar vários
segundos para responder e gerar alto consumo de CPU e RAM, afetando outros processos e
a saúde do sistema como um todo.
Comportamento Interno do OFFSET no SQL
Diferente do WHERE, que filtra registros logo no início da execução da query, o OFFSET
apenas pula os resultados já processados. Isso significa que, para OFFSET 500 LIMIT 20,
o banco busca os 520 registros e só retorna os 20 após o 500º, ao invés de simplesmente
limitar a consulta desde o começo.
Atenção
OFFSET não indica ao banco de dados quais linhas exatamente ignorar em relação à
ordenação do resultado, pois o banco não guarda referência sequencial dos registros
consultados.
Quando usar Offset-Based Pagination de forma segura
Offset-Based Pagination é suficiente e eficiente quando você trabalha com um volume
pequeno ou moderado de dados (centenas a poucos milhares de registros), onde o overhead
de ignorar múltiplos registros não afeta significativamente o desempenho. Também é útil
quando a navegação direta entre páginas exatas é importante para o usuário.
- Passo 1: Analise o tamanho máximo da sua tabela antes de optar
por OFFSET. - Passo 2: Considere a necessidade de navegação direta entre
páginas. - Passo 3: Utilize índices para garantir que o ORDER BY acompanhe
o OFFSET de modo eficiente.
Paginação com Scroll Infinito: o que muda?
Em interfaces modernas, cada vez mais comuns são as listas com carregamento contínuo
(scroll infinito), como exemplos do Twitter e Instagram. Nesses casos, o usuário não
seleciona páginas, mas carrega os itens seguintes conforme rola a tela. Aqui, a
abordagem com OFFSET rapidamente se torna ineficiente.
Entrada do Cursor-Based Pagination
Cursor-Based Pagination utiliza um campo ordenável do registro (ex: ID sequencial ou
data de publicação) como referência para buscar novos elementos, usando um WHERE ao
invés de OFFSET. Assim, ao buscar a próxima "página" de dados, só são buscados registros
após o último exibido, aumentando a performance drasticamente, especialmente em grandes
volumes.
Como funciona Cursor-Based Pagination
Em vez de pular um número fixo de registros, você envia o valor do último campo
ordenável exibido (como o ID ou data) como cursor para a próxima consulta. Por exemplo,
WHERE id > 400, LIMIT 20. Assim, só os registros após o último são trazidos, sem
custo extra de leitura de dados.
- Passo 1: Escolha um campo único e ordenável da sua tabela.
- Passo 2: Use o valor do último item retornado em cada
carregamento como cursor para a próxima busca. - Passo 3: Implemente no frontend o armazenamento deste cursor ao
carregar mais registros.
Limitações do Cursor-Based Pagination
Ao contrário do Offset, o Cursor-Based Pagination não permite navegação direta para
qualquer página arbitrária, pois depende do último cursor recebido. Ele é ideal para
fluxos contínuos como scroll infinito, mas não para cenários onde ir para a página X é
fundamental.
Atenção
Cursor-Based depende de um campo ordenável confiável (ID incremental, data, número
serial). Em bancos onde o identificador é um UUID v4 (sem ordenação temporal), não é
possível usar essa abordagem corretamente – prefira UUID v7 ou campos com data/hora.
Desafios com IDs não ordenáveis e alternativas
Quando o campo de identificação não é sequencial (como UUID v4), o banco não consegue
definir a ordem natural dos registros para Cursor-Based Pagination. Isso impede ordenar
ou filtrar registros pela ordem de criação. Para casos assim, o recomendado é adotar um
campo de data ou migrar para UUID v7 (ordenável por tempo).
Atenção
Não tente forçar paginação cursor por UUID v4: as páginas se tornam imprevisíveis e
até inconsistentes para o usuário.
Comparando Offset-Based e Cursor-Based Pagination
Offset-Based Pagination
Paginação tradicional usando OFFSET e LIMIT para navegar por páginas exatas.
+ Prós
- • Permite pular para qualquer página rapidamente
- • Simples de implementar
− Contras
- • Degrada progressivamente a performance com o aumento de registros
- • Alto consumo de recursos em páginas avançadas
Cursor-Based Pagination
Utiliza um campo ordenável como referência, buscando sempre a partir do elemento mais recente processado.
+ Prós
- • Performance estável, mesmo com milhões de registros
- • Ideal para scroll infinito e APIs modernas
− Contras
- • Impossibilita navegação por página exata
- • Depende de campo ordenável confiável
Ferramentas e recursos para implementar e testar paginação
PostgreSQL Explain Analyze
Analisa planos de execução de queries e ajuda a identificar gargalos de OFFSET.
Prisma ORM
ORM moderno para Node.js com suporte a both Offset e Cursor Based Pagination.
UUID v7 Docs
Leia sobre o UUID v7 para IDs ordenáveis.
SQLite Database Browser
Ferramenta grátis para explorar pequenas bases e simular diferentes métodos de paginação.
Resumo: Escolhendo a melhor estratégia de paginação
Para aplicações e painéis onde o volume de dados é pequeno e a navegação direta por
página é necessária, Offset-Based Pagination ainda é eficiente. Em APIs modernas, listas
longas e scroll infinito, Cursor-Based Pagination entrega performance muito superior e
estabilidade, sendo indispensável para grandes volumes de dados.
Erros comuns e como evitá-los
- Erro 1: Usar OFFSET em tabelas com milhões de linhas sem
índices robustos. - Erro 2: Implementar cursor em campos não ordenáveis ou não
únicos. - Erro 3: Não considerar plano de execução de queries em
ambientes de produção.
Checklist de implementação de paginação eficiente
Checklist de Implementação
- Identifique o volume de registros envolvidos na listagem por tabela
- Avalie se a experiência exige navegação direta entre páginas ou scroll infinito
- Selecione o método de paginação de acordo com o cenário: OFFSET ou Cursor
- Garanta existência de índices nos campos usados para ordenação ou cursor
- Teste performance de queries reais com Explain ou ferramentas similares
- Evite cursors em campos UUID v4; use UUID v7, datas ou incrementais
- Implemente e monitore consumo de recursos no banco e app
Perguntas frequentes
Offset vs cursor pagination?
Cursor escala melhor em feeds grandes. Offset é simples e quebra com inserts.
Quando offset ok?
Admin lists pequenas.
API pública?
Prefira cursor/keyset.
Com React Query?
Use pageParam/cursor consistente.
Continue explorando
Continue: Curso Node.js · Clean Vertical Slice · Programa CrazyStack · Cursos CrazyStack.
Perguntas frequentes
Offset vs cursor pagination?
Cursor escala melhor em feeds grandes. Offset é simples e quebra com inserts.
Quando offset ok?
Admin lists pequenas.
API pública?
Prefira cursor/keyset.
Com React Query?
Use pageParam/cursor consistente.
O que é Offset-Based Pagination?
O modelo Offset-Based Pagination utiliza os comandos OFFSET e LIMIT nas queries para retornar apenas um subconjunto de registros, permitindo navegar entre "páginas" de dados com facilidade. Por exemplo, ao consultar a página 2 com 10 itens por página, você faz OFFSET 10 LIMIT 10, pulando os 10 primeiros e pegando os próximos 10 resultados.
Por que o OFFSET traz problemas de performance?
Quando se trabalha com tabelas que possuem milhões de registros, o banco precisa percorrer (e processar) todos os registros até o número do OFFSET antes de retornar o resultado solicitado. Isso faz com que consultas em páginas distantes da primeira aumentem exponencialmente em tempo de execução e uso de memória, tornando o processo cada vez mais lento e custoso.
Quando usar Offset-Based Pagination de forma segura
Offset-Based Pagination é suficiente e eficiente quando você trabalha com um volume pequeno ou moderado de dados (centenas a poucos milhares de registros), onde o overhead de ignorar múltiplos registros não afeta significativamente o desempenho. Também é útil quando a navegação direta entre páginas exatas é importante para o usuário.
Paginação com Scroll Infinito: o que muda?
Em interfaces modernas, cada vez mais comuns são as listas com carregamento contínuo (scroll infinito), como exemplos do Twitter e Instagram. Nesses casos, o usuário não seleciona páginas, mas carrega os itens seguintes conforme rola a tela. Aqui, a abordagem com OFFSET rapidamente se torna ineficiente.
Como funciona Cursor-Based Pagination
Em vez de pular um número fixo de registros, você envia o valor do último campo ordenável exibido (como o ID ou data) como cursor para a próxima consulta. Por exemplo, WHERE id > 400, LIMIT 20. Assim, só os registros após o último são trazidos, sem custo extra de leitura de dados.