14 de abr. de 2025·7 min de leitura

Indexação para painéis de administração: otimize primeiro os filtros mais usados

Indexação para painéis administrativos: otimize os filtros que os usuários mais clicam — status, responsável, intervalos de data e busca de texto — com base em padrões reais de queries.

Indexação para painéis de administração: otimize primeiro os filtros mais usados

Por que os filtros do painel de administração ficam lentos

Painéis de administração normalmente começam rápidos. Você abre uma lista, rola, clica em um registro e segue. A lentidão aparece quando as pessoas filtram do jeito que realmente trabalham: "Apenas tickets abertos", "Atribuído a Maya", "Criado na semana passada", "Order ID contém 1047". Cada clique provoca espera, e a lista começa a ficar travada.

A mesma tabela pode ser rápida para um filtro e dolorosamente lenta para outro. Um filtro de status pode tocar apenas um pequeno subconjunto de linhas e retornar rápido. Um filtro "criado entre duas datas" pode forçar o banco a ler um grande intervalo. Um filtro por responsável pode ser ok sozinho, mas desacelerar quando combinado com status e ordenação.

Índices são o atalho que o banco usa para encontrar linhas correspondentes sem ler a tabela inteira. Mas índices não são gratuitos. Ocupam espaço e tornam inserts/updates um pouco mais lentos. Adicionar demais pode desacelerar gravações e ainda não resolver o gargalo real.

Em vez de indexar tudo, priorize os filtros que:

  • são usados constantemente
  • tocam muitas linhas
  • criam espera perceptível
  • podem ser melhorados com índices simples e bem alinhados

Isso mantém o foco estreito. As primeiras reclamações de desempenho em listas administrativas quase sempre vêm dos mesmos quatro tipos de filtro: status, assignee, intervalos de datas e campos de texto. Quando você entende por que eles se comportam de forma diferente, os próximos passos ficam claros: veja os padrões reais de query, adicione o menor índice que os corresponda e verifique se você melhorou o caminho lento sem criar novos problemas.

Os padrões de query por trás do trabalho real em admin

Painéis administrativos raramente ficam lentos por um único relatório gigantesco. Ficam lentos porque algumas telas são usadas o dia todo, e essas telas rodam muitas queries pequenas repetidamente.

Times de operações normalmente vivem em algumas filas de trabalho: tickets, pedidos, usuários, aprovações, pedidos internos. Nessas páginas, os filtros se repetem:

  • Status, porque espelha o fluxo de trabalho (New, Open, Pending, Done)
  • Assignee, porque times precisam de "meus itens" e "não atribuídos"
  • Intervalos de data, porque alguém sempre pergunta "o que aconteceu semana passada?"
  • Busca, para pular para um item conhecido (número do pedido, email) ou para escanear texto (notas, pré-visualizações)

O trabalho do banco depende da intenção:

  • Navegar pelos mais novos é um padrão de varredura. Normalmente é algo como "mostrar os itens mais recentes, talvez filtrados por status, ordenados por created time" e é paginado.
  • Encontrar um item específico é um padrão de lookup. O admin já tem um ID, email, número de ticket ou referência e espera que o banco vá direto a um pequeno conjunto de linhas.

Painéis administrativos também combinam filtros de maneiras previsíveis: "Open + Unassigned", "Pending + Assigned to me" ou "Completed nos últimos 30 dias". Índices funcionam melhor quando batem nessas formas reais de query, não quando apenas cobrem uma lista de colunas.

Se você constrói ferramentas administrativas em AppMaster (appmaster.io), esses padrões geralmente são visíveis ao observar as telas de lista mais usadas e seus filtros padrão. Isso facilita indexar o que realmente impulsiona o trabalho diário, não o que parece bom no papel.

Como escolher o que indexar primeiro

Trate indexação como triagem. Não comece indexando toda coluna que aparece em um dropdown de filtro. Comece com poucas queries que rodam constantemente e irritam as pessoas.

Encontre os filtros que as pessoas realmente usam

Otimizar um filtro que ninguém toca é trabalho perdido. Para encontrar os caminhos quentes, combine sinais:

  • Analytics da UI: quais telas recebem mais visualizações, quais filtros são clicados mais
  • Logs do banco ou API: queries mais frequentes e os percentis mais lentos
  • Feedback interno: "a busca está lenta" geralmente aponta para uma tela específica
  • A lista de entrada padrão: o que roda assim que um admin abre o painel

Em muitos times, essa vista padrão é algo como "Open tickets" ou "New orders". Ela roda toda vez que alguém atualiza, troca de aba ou retorna depois de responder.

Agrupe queries por formato, não por nome de campo

Antes de adicionar um índice, agrupe suas queries comuns por como elas se comportam. A maioria das queries de lista administrativa cai em alguns buckets:

  • Filtros de igualdade: status = 'open', assignee_id = 42
  • Filtros de intervalo: created_at entre duas datas
  • Ordenação e paginação: ORDER BY created_at DESC e buscar a página 2
  • Buscas em texto: igualdade exata (order number), prefixo (email começa com), ou contains

Escreva a forma de cada tela principal, incluindo WHERE, ORDER BY e paginação. Duas queries que parecem similares na UI podem se comportar muito diferente no banco.

Escolha um pequeno lote inicial

Comece com um alvo prioritário: a query da lista padrão que carrega primeiro. Depois escolha mais 2 ou 3 queries de alta frequência. Isso geralmente reduz os maiores atrasos sem transformar seu banco em um museu de índices.

Exemplo: um time de suporte abre uma lista de Tickets filtrada por status = 'open', ordenada pelos mais novos, com assignee e intervalo de data opcionais. Otimize exatamente essa combinação primeiro. Quando ficar rápida, passe para a próxima tela com base no uso.

Indexando o filtro de status sem exagerar

Status é um dos primeiros filtros que as pessoas adicionam, e também um dos mais fáceis de indexar de forma errada.

A maioria dos campos de status tem baixa cardinalidade: poucos valores (open, pending, closed). Um índice ajuda mais quando consegue reduzir os resultados a uma fatia pequena da tabela. Se 80% a 95% das linhas compartilham o mesmo status, um índice só em status muitas vezes não muda muito. O banco ainda precisa ler uma grande porção de linhas, e o índice adiciona overhead.

Você normalmente sente o benefício quando:

  • um status é raro (por exemplo, escalado)
  • status é combinado com outra condição que reduz bastante o conjunto
  • status mais ordenação corresponde a uma vista comum

Um padrão comum é "me mostre itens abertos, mais recentes primeiro." Nesse caso, indexar o filtro e a ordenação juntos muitas vezes é melhor do que indexar status isoladamente.

As combinações que tendem a valer a pena primeiro:

  • status + updated_at (filtrar por status, ordenar por mudanças recentes)
  • status + assignee_id (vistas de fila de trabalho)
  • status + updated_at + assignee_id (somente se essa vista exata for muito usada)

Índices parciais são um bom meio-termo quando um status domina. Se "open" é a vista principal, indexe apenas as linhas open. O índice fica menor e o custo de escrita permanece mais baixo.

-- PostgreSQL example: index only open rows, optimized for newest-first lists
CREATE INDEX CONCURRENTLY tickets_open_updated_idx
ON tickets (updated_at DESC)
WHERE status = 'open';

Um teste prático: rode a query administrativa lenta com e sem o filtro de status. Se ela for lenta em ambos os casos, um índice só em status não vai resolver. Foque na ordenação e no segundo filtro que realmente reduz a lista.

Filtragem por assignee: índices de igualdade e combinações comuns

Do protótipo à produção
Crie apps web e móveis prontos para produção, com código-fonte limpo e regenerável.
Experimentar agora

Na maioria dos painéis, assignee é um ID de usuário armazenado no registro: uma foreign key como assignee_id. Esse é um filtro de igualdade clássico e muitas vezes é um ganho rápido com um índice simples.

Assignee também aparece junto com outros filtros porque reflete como as pessoas trabalham. Um líder de suporte pode filtrar para "Atribuído ao Alex" e depois restringir para "Open" para ver o que ainda precisa de atenção. Se essa vista for lenta, normalmente você precisa de mais que um índice de coluna única.

Um bom ponto de partida é um índice composto que corresponda à combinação de filtro comum:

  • (assignee_id, status) para "meus itens abertos"
  • (assignee_id, status, updated_at) se a lista também for ordenada por atividade recente

A ordem importa em índices compostos. Coloque filtros de igualdade primeiro (frequentemente assignee_id, depois status) e a coluna de ordenação ou intervalo por último (updated_at). Isso alinha com o que o banco pode usar eficientemente.

Itens não atribuídos são um achado comum. Muitos sistemas representam "unassigned" como NULL em assignee_id, e gestores filtram por isso frequentemente. Dependendo do banco e do formato da query, NULL pode alterar o plano o bastante para que um índice que funciona bem para itens atribuídos pareça inútil para não atribuídos.

Se não atribuídos forem um fluxo importante, escolha uma abordagem clara e teste:

  • Mantenha assignee_id nullable, mas garanta que WHERE assignee_id IS NULL seja testado e indexado quando necessário.
  • Use um valor dedicado (como um usuário especial "Unassigned") somente se fizer sentido no seu modelo de dados.
  • Adicione um índice parcial para linhas não atribuídas se o banco suportar.

Se você estiver construindo um painel em AppMaster (appmaster.io), ajuda registrar os filtros e ordenações exatas que seu time usa mais e espelhar esses padrões com um pequeno conjunto de índices bem escolhidos em vez de indexar todo campo disponível.

Intervalos de data: índices que correspondem a como as pessoas filtram

Crie lógica sem código extra
Adicione lógica de negócio com workflows drag-and-drop em vez de conectar endpoints customizados manualmente.
Experimentar AppMaster

Filtros de data costumam aparecer como presets rápidos como "últimos 7 dias" ou "últimos 30 dias", além de um seletor customizado com início e fim. Parecem simples, mas podem desencadear trabalhos muito diferentes em tabelas grandes.

Primeiro, seja claro sobre qual coluna de timestamp as pessoas realmente querem. Use:

  • created_at para vistas de "itens novos"
  • updated_at para vistas de "alterações recentes"

Coloque um índice btree normal nessa coluna. Sem ele, todo clique em "últimos 30 dias" pode virar um full table scan.

Presets de intervalo geralmente parecem created_at >= now() - interval '30 days'. Isso é uma condição de intervalo, e um índice em created_at pode ser usado eficientemente. Se a UI também ordena pelos mais recentes, casar a direção da ordenação (por exemplo, created_at DESC no PostgreSQL) pode ajudar em listas muito usadas.

Quando intervalos de data se combinam com outros filtros (status, assignee), seja seletivo. Índices compostos são ótimos quando a combinação é comum. Caso contrário, aumentam o custo de escrita sem muitos benefícios.

Regras práticas:

  • Se a maioria das vistas filtra por status e depois data, (status, created_at) pode ajudar.
  • Se o status é opcional mas a data aparece sempre, mantenha um índice simples em created_at e evite muitos compostos.
  • Não crie todas as combinações. Cada índice novo aumenta armazenamento e desacelera escritas.

Fuso horário e fronteiras causam muitos bugs de "registros faltando". Se usuários escolhem datas (sem hora), decida como interpretar a data final. Um padrão seguro é início inclusivo e fim exclusivo: created_at >= start e created_at < end_next_day. Armazene timestamps em UTC e converta a entrada do usuário para UTC antes de consultar.

Exemplo: um admin escolhe 10 a 12 de jan e espera ver itens de todo o dia 12. Se sua query usar <= '2026-01-12 00:00', você vai perder quase tudo de 12 de jan. O índice está ok, mas a lógica de fronteira está errada.

Campos de texto: busca exata vs busca por conteúdo

Busca em texto é onde muitos painéis administrativos ficam lentos, porque as pessoas esperam que uma caixa encontre tudo. O primeiro ajuste é separar duas necessidades diferentes: busca exata (rápida e previsível) vs busca por conteúdo/contains (flexível, mas mais custosa).

Busca exata inclui order ID, ticket number, email, telefone ou uma referência externa. Esses são perfeitos para índices normais. Se admins costumam colar um ID ou email, um índice simples mais uma query de igualdade pode deixar a busca instantânea.

Busca por conteúdo é quando alguém digita um fragmento como "refund" ou "john" e espera achar em nomes, notas e descrições. Muitas vezes é implementado como LIKE %term%. O curinga no começo impede que um índice B-tree estreito ajude, então o banco escaneia muitas linhas.

Uma forma prática de construir busca sem sobrecarregar o banco:

  • Faça busca exata um caminho primário (ID, email, username) e mostre isso claramente.
  • Para busca "começa com" (term%), um índice padrão pode ajudar e costuma ser suficiente para nomes.
  • Adicione busca por conteúdo só quando logs ou reclamações mostrarem necessidade.
  • Quando adicionar, use a ferramenta certa (full-text search do PostgreSQL ou índices de trigram) em vez de esperar que um índice normal solucione LIKE %term%.

Regras de entrada importam mais do que muitas equipes esperam. Elas reduzem carga e tornam resultados consistentes:

  • Defina tamanho mínimo para busca por conteúdo (por exemplo, 3+ caracteres).
  • Normalize caixa (case) ou use comparações case-insensitive consistentemente.
  • Remova espaços iniciais/finais e colapse espaços repetidos.
  • Trate emails e IDs como exatos por padrão, mesmo se inseridos numa busca geral.
  • Se um termo for muito amplo, peça ao usuário para ser mais específico em vez de rodar uma query enorme.

Um pequeno exemplo: um gerente de suporte busca "ann" para achar um cliente. Se seu sistema roda LIKE %ann% em notas, nomes e endereços, pode escanear milhares de registros. Se você primeiro verifica campos exatos (email ou customer ID) e só depois recorre a um índice de texto mais inteligente quando necessário, a busca se mantém rápida sem transformar cada consulta em um exercício pesado para o banco.

Um fluxo passo a passo para adicionar índices com segurança

Faça filtros comuns parecerem instantâneos
Crie ferramentas internas que combinam status, responsável e filtros de data com ordenação consistente.
Iniciar app

Índices são fáceis de adicionar e fáceis de se arrepender. Um fluxo seguro mantém o foco nos filtros que seus admins dependem e evita índices "talvez úteis" que tornam as escritas lentas depois.

Comece com uso real. Extraia as queries principais de duas formas:

  • as queries mais frequentes
  • as queries mais lentas

Para painéis administrativos, geralmente são páginas de lista com filtros e ordenação.

Em seguida, capture a forma exata da query como o banco a vê. Anote o WHERE e o ORDER BY precisos, incluindo direção de ordenação e combinações comuns (por exemplo: status = 'open' AND assignee_id = 42 ORDER BY created_at DESC). Pequenas diferenças podem mudar qual índice ajuda.

Use um loop simples:

  • Escolha uma query lenta e uma mudança de índice para testar.
  • Adicione ou ajuste um único índice.
  • Re-meça com os mesmos filtros e a mesma ordenação.
  • Verifique se inserts e updates não ficaram visivelmente mais lentos.
  • Mantenha a mudança apenas se ela melhorar claramente a query alvo.

Paginação merece checagem à parte. Paginação baseada em offset (OFFSET 20000) costuma ficar mais lenta conforme você avança, mesmo com índices. Se usuários pulam para páginas muito profundas, considere paginação por cursor ("mostrar itens antes deste timestamp/id") para que o índice faça trabalho consistente em grandes tabelas.

Por fim, mantenha um registro pequeno para que sua lista de índices continue compreensível meses depois: nome do índice, tabela, colunas (e ordem) e a query que ele suporta.

Erros comuns de indexação em painéis administrativos

Construa pesquisa do jeito certo
Entregue uma UI administrativa que suporte buscas exatas e pesquisa de texto controlada desde o primeiro dia.
Criar painel

A forma mais rápida de tornar um painel administrativo lento é adicionar índices sem checar como as pessoas realmente filtram, ordenam e paginam resultados. Índices custam espaço e adicionam trabalho a cada insert e update.

Erros que aparecem com mais frequência

Esses padrões causam a maioria dos problemas:

  • Indexar toda coluna "por precaução".
  • Criar um índice composto com ordem de colunas errada.
  • Ignorar ordenação e paginação.
  • Esperar que um índice normal resolva LIKE '%term%'.
  • Deixar índices antigos após mudanças na UI.

Um cenário comum: um time filtra tickets por Status = Open, ordena por updated time e pagina. Se você só adicionou um índice em status, o banco ainda pode ter que reunir todos os tickets abertos e ordenar. Um índice que combine filtro e ordenação pode devolver a página 1 rapidamente.

Maneiras rápidas de detectar esses problemas

Antes e depois de mudanças na UI administrativa, faça uma revisão rápida:

  • Liste os filtros principais e a ordenação padrão, e confirme se há um índice que combine WHERE + ORDER BY.
  • Verifique wildcards no início (LIKE '%term%') e decida se busca por conteúdo é realmente necessária.
  • Procure índices duplicados ou sobrepostos.
  • Monitore índices não usados por um tempo e depois remova-os quando tiver certeza de que não são necessários.

Se você constrói painéis em AppMaster com PostgreSQL, faça essa revisão como parte do deploy de novas telas. Os índices certos costumam surgir diretamente dos filtros e ordenações que sua UI realmente usa.

Verificações rápidas e próximos passos

Antes de adicionar mais índices, confirme que os que você já tem ajudam as exatas combinações de filtro que as pessoas usam todo dia. Um bom painel administrativo parece instantâneo nos caminhos comuns, não em buscas raras.

Algumas checagens resolvem a maioria dos problemas:

  • Abra as combinações de filtro mais comuns (status, assignee, intervalo de datas e ordenação padrão) e confirme que permanecem rápidas conforme a tabela cresce.
  • Para cada vista lenta, verifique se a query usa um índice que combine WHERE e ORDER BY, não apenas uma parte.
  • Mantenha a lista de índices pequena o suficiente para que você explique a utilidade de cada um em uma frase.
  • Observe ações com muita escrita (create, update, mudança de status). Se elas ficaram mais lentas após indexar, talvez haja índices demais ou sobrepostos.
  • Decida o que "busca" significa na sua UI: correspondência exata, prefixo ou contains. Seu plano de indexação precisa refletir essa escolha.

Um próximo passo prático é escrever seus caminhos dourados em frases simples, como: "Agentes de suporte filtram tickets abertos, atribuídos a mim, últimos 7 dias, ordenados pelos mais novos." Use essas frases para projetar um pequeno conjunto de índices que suportem claramente esses casos.

Se você ainda está no início, ajuda modelar dados e filtros padrão antes de criar muitas telas. Com AppMaster (appmaster.io), você pode iterar nas views administrativas rapidamente e depois adicionar os poucos índices que correspondem ao uso real, quando os caminhos quentes estiverem claros.

FAQ

O que devo indexar primeiro em um painel administrativo?

Comece pelas queries que rodam constantemente: a vista de lista padrão que os administradores veem ao abrir o painel, mais 2–3 filtros que são clicados o dia todo. Meça frequência e impacto (mais usadas e mais lentas) e crie índices apenas para reduzir claramente o tempo de espera nessas formas de query específicas.

Por que um filtro é rápido e outro extremamente lento na mesma tabela?

Porque filtros diferentes exigem quantidades distintas de trabalho. Alguns filtros reduzem para um conjunto pequeno de linhas, enquanto outros atingem uma grande faixa ou precisam ordenar muitos resultados. Assim, uma query pode usar um índice eficientemente enquanto outra ainda precisa escanear e ordenar muitos dados.

Devo sempre adicionar um índice na coluna de status?

Nem sempre. Se a maior parte das linhas tem o mesmo status, um índice só em status geralmente não reduz muito o trabalho. Ele ajuda mais quando um status é raro ou quando você indexa status junto com a ordenação ou outro filtro que realmente encolhe o conjunto de resultados.

Como acelerar a vista comum “Itens abertos, mais recentes primeiro”?

Use um índice composto que corresponda ao que as pessoas realmente fazem, por exemplo filtrar por status e ordenar por atividade recente. No PostgreSQL, um índice parcial pode ser uma boa opção quando um status domina, pois mantém o índice pequeno e focado no fluxo comum.

Qual a melhor forma de indexar filtragem por responsável (assignee)?

Um índice simples em assignee_id costuma ser um ganho rápido, pois é um filtro de igualdade. Se “meus itens abertos” for um fluxo central, um índice composto que comece com assignee_id e depois inclua status (e opcionalmente a coluna de ordenação) geralmente tem melhor desempenho do que índices separados por coluna.

Por que filtrar por “não atribuído” continua lento mesmo após indexar assignee_id?

Não é o mesmo que WHERE assignee_id = 123. Unassigned costuma ser representado por NULL e WHERE assignee_id IS NULL pode gerar um plano diferente. Se filas de não atribuídos importam, teste essa query especificamente e adote uma estratégia de índice que a suporte, por exemplo um índice parcial para linhas não atribuídas se o banco permitir.

Como devo indexar filtros por intervalo de datas como “últimos 7 dias”?

Adicione um índice btree na coluna de timestamp que as pessoas realmente usam: normalmente created_at para itens novos e updated_at para alterações recentes. Sem esse índice, um clique em “últimos 30 dias” pode virar um full table scan. Se também houver ordenação por mais recentes, combinar direção de ordenação no índice pode ajudar em listas muito usadas.

Como evitar bugs de fuso horário e data final em filtros de data administrativos?

A maioria dos bugs de registros faltando vem de fronteiras de data, não do índice. Um padrão confiável é começo inclusivo e fim exclusivo: converta as datas do usuário para UTC e consulte >= start e < end_next_day, evitando assim dropar os eventos ocorridos ao longo do dia final.

Por que um índice normal não resolve busca por “contém” em campos de texto?

Porque LIKE %term% com wildcard no começo impede o uso do índice btree, forçando um scan em muitas linhas. Trate buscas exatas (ID, e-mail, número de pedido) como caminho rápido e só adicione busca por conteúdo quando necessário, usando ferramentas adequadas (Full Text Search do PostgreSQL, trigram indexes), em vez de esperar que um índice normal resolva LIKE %term%.

Posso apenas indexar todas as colunas filtráveis para evitar lentidões?

Muitos índices aumentam armazenamento e tornam inserts/updates mais lentos, e você ainda pode não acertar o gargalo se o índice não corresponder ao padrão WHERE + ORDER BY. Um loop mais seguro é mudar um índice por vez, re-medir a mesma query lenta e manter apenas as alterações que claramente melhoram o caminho crítico.\n\nSe você constrói telas administrativas em AppMaster, registre os filtros e ordenações exatas que sua equipe usa mais e então adicione um pequeno conjunto de índices que espelhem essas vistas reais em vez de indexar todo campo disponível.

Fácil de começar
Criar algo espantoso

Experimente o AppMaster com plano gratuito.
Quando estiver pronto, você poderá escolher a assinatura adequada.

Comece
Indexação para painéis de administração: otimize primeiro os filtros mais usados | AppMaster