Pular para o conteúdo
<Dev.Lucas />
Voltar ao blog

Publicado em 31 de outubro de 2026 · 2 min de leitura

Uma busca de catálogo que acha “Miu Miu”: FULLTEXT no MariaDB

Como a busca do Resolvi Desapegar usa um índice InnoDB próprio, normalização simétrica e degraus de fallback, e por que filtrar na view custava 1,5 s.

MariaDBBuscaPerformancePHP

O catálogo do Resolvi Desapegar tem peças de luxo, e boa parte das buscas é por marca: “bolsa chanel”, “brinco vivara”, “bolsa miu miu”. Esse último caso é o difícil. Sem cuidado, “miu” é curto demais para o índice de texto, a busca vira apenas “bolsa” e devolve o catálogo inteiro.

O problema do tamanho mínimo da palavra

A maioria das tabelas do banco é MyISAM, e o FULLTEXT do MyISAM ignora palavras menores que ft_min_word_len (4, no servidor em questão). O InnoDB usa outro parâmetro, innodb_ft_min_token_size, configurado com 3, e portanto indexa “miu”. A solução foi não buscar na tabela de produtos: a busca tem uma tabela própria, em InnoDB, só para isso.

Uma tabela derivada, nunca a fonte da verdade

A tabela produtos_busca guarda, por produto, um texto normalizado com título, marca, categorias e referência, e a descrição à parte. Ela é derivada: se for perdida, é reconstruída a partir dos produtos. O índice é atualizado quando um produto é salvo no painel, por um processo incremental a cada 5 minutos e por uma reconstrução completa de madrugada.

Normalizar dos dois lados do mesmo jeito

O texto indexado e o termo digitado passam pela mesma função: minúsculas, sem acento, plural reduzido ao radical, palavras vazias removidas. Se uma das pontas muda sem a outra, a busca silenciosamente deixa de achar coisas, então qualquer alteração na normalização exige reindexar tudo.

-- Exemplo simplificado da ideia (não é o SQL do projeto)
SELECT produto_id
FROM produtos_busca
WHERE MATCH(doc) AGAINST ('+bolsa +miu' IN BOOLEAN MODE)
LIMIT 40;

Degraus: do mais preciso ao mais permissivo

A busca começa exigindo todos os termos no título. Se não houver resultado, exige todos os termos incluindo a descrição, depois aceita qualquer termo e, por último, cai num LIKE. Cada degrau só roda se o anterior voltou vazio, o que mantém a relevância alta sem devolver o catálogo inteiro quando a pessoa digita algo muito específico.

O custo escondido da view

A listagem de produtos usa uma view com GROUP BY. No MariaDB, uma view assim é materializada por completo a cada consulta, cerca de 16 mil linhas, e filtrar nela custava em torno de 1,5 s, mesmo com índice na tabela base. O padrão que resolveu foi em dois passos: filtrar a tabela base, coletar os ids e só então consultar a view com IN (lista literal de ids). A mesma consulta caiu para cerca de 25 ms. Uma subconsulta IN (SELECT ...) não serve, porque o otimizador volta a materializar tudo.

O que fica de lição

  • Confirme o motor da tabela antes de assumir transações, chaves estrangeiras ou o comportamento do FULLTEXT.
  • Índice de busca é um dado derivado: planeje como reconstruí-lo por inteiro.
  • Normalize consulta e índice com a mesma função, e teste as duas pontas juntas.
  • Meça a consulta real. Um índice na tabela base não ajuda se o filtro está na view.