
Praticamente todo banco de dados↳Banco de dados134 conteúdosSQL ou NoSQL: eis a questão!!Data · mar 2020Banco de dados: como organizar e dar segurança para milhões de dados de loteriasData · mai 20215 serviços gratuitos na cloud para bancos de dados PostgresData · fev 2025Ver tudo em Data → hoje anuncia busca vetorial, e quase todos publicam um post com um número grande de consultas por segundo. O problema, segundo Evgeniy Patlan em artigo no Percona Database Blog, é que esses números raramente podem ser verificados, porque a informação que os torna significativos costuma faltar. Para atacar isso, a Percona construiu uma ferramenta chamada vector-bench: o operador nomeia os motores, cada um é montado a partir de versões fixadas, roda no mesmo container, nos mesmos núcleos, com os mesmos dados e as mesmas medições, e gera um relatório.
Para o DBA brasileiro que começa a receber demanda de aplicações com IA↳Inteligência artificial440 conteúdosUX e IA: Transformando Experiências Digitais com Inteligência ArtificialProduto & UX · jan 2025MCP: O que é e por que você vai ouvir falar disso em breve?AI · jul 2025IA generativa e a urgência de reconstruir nossa relação com a verdadeAI · jun 2025Ver tudo em AI → e RAG, a mensagem central é sóbria e vale ser repetida: em busca vetorial, toda medição é um par. Throughput sem recall ao lado não diz nada, e recall sem throughput é de graça.
O que está sendo indexado
Um embedding é um array de floats de tamanho fixo que sai de um modelo. A propriedade útil é que entradas semanticamente parecidas ficam próximas quando se mede a distância entre elas. Duas métricas cobrem quase tudo: L2, a distância euclidiana clássica estendida a muitas dimensões, e similaridade de cosseno, que mede o ângulo entre dois vetores e ignora o comprimento. Qual delas se aplica é decisão do modelo que gerou os embeddings, não uma escolha de query time. Trocar uma pela outra produz resultado sem sentido.
A consulta desejada é simples de enunciar: "as 10 linhas cujos vetores estão mais próximos deste".
SELECT id FROM documents ORDER BY distance(embedding, ?) LIMIT 10;O problema é responder isso de forma exata. Calcular a distância do vetor de consulta para cada linha e ordenar é, na prática, um full table scan com muita aritmética por cima. Nenhum B-tree ou hash ajuda: eles não sabem ordenar um milhão de pontos por proximidade em 1536 dimensões. Um índice vetorial abre mão da exatidão para evitar isso, olhando alguns milhares de candidatos promissores em vez de todas as linhas. É a busca aproximada de vizinhos, ou ANN, e ela "quase sempre" acerta. Medir esse "quase sempre" é o trabalho do benchmark, e depende de conhecer a ground truth: os vizinhos verdadeiros de cada consulta, calculados uma vez por força bruta, sem índice. É justamente esse número que a maioria das alegações de vector search omite.
HNSW e IVF: dois desenhos opostos
Quase todo banco escolheu um de dois designs, e a escolha define o que se pode ajustar.
HNSW (Hierarchical Navigable Small World) é um grafo de vetores em camadas, parente próximo de uma skip list. A camada de topo tem poucos nós com links longos; cada camada abaixo tem mais nós e links mais curtos. A busca começa no topo, pula sempre para o vizinho mais próximo da consulta e desce de camada quando não há nada mais perto. Dois parâmetros importam: M, quantos links cada nó mantém (fixado na construção, mais alto significa melhor recall a custo de build mais lento e índice maior), e ef_search, quantos candidatos a busca acompanha enquanto caminha. Este último é variável de sessão, ajustável por query. Há ainda ef_construction, a mesma ideia aplicada na construção, que nem todo motor expõe.
IVF (Inverted File) particiona em vez de ligar. Na construção, agrupa os vetores em nlist clusters com um representante no centro; na consulta, compara com os representantes, escolhe os nprobe clusters mais próximos e busca só dentro deles. Constrói bem mais rápido e usa menos memória, mas costuma dar recall pior na mesma velocidade, e erra quando o vizinho verdadeiro fica logo fora dos clusters visitados. A Percona testa só motores rodando HNSW, que é o que a maioria dos bancos entregou; motores só-IVF ficam num balde separado para não medir a distância entre dois algoritmos em vez da qualidade da implementação.
Por que um número nunca basta
Recall não é propriedade do motor, é um ajuste, e ef_search é o botão. O artigo mostra o mesmo índice HNSW, na mesma máquina, com os mesmos dados, variando apenas quantos candidatos a busca acompanha:
ef_search=10 3.678 queries/seg recall 0,9593
ef_search=800 409 queries/seg recall 0,9987Manter 800 candidatos em vez de 10 acha resposta melhor e leva nove vezes mais tempo. As duas linhas são medições honestas do mesmo índice. Por isso "nosso banco faz 3.678 consultas vetoriais por segundo" não informa nada: não se sabe com que frequência ele devolvia linhas erradas, e quem cita o número talvez também não saiba. O inverso é igualmente vazio, porque recall 1,0 está sempre disponível: basta desligar o índice e varrer a tabela.
As armadilhas que inflam qualquer número
O benchmark cria uma tabela por motor, com um id, uma coluna tag inteira usada só nos testes filtrados, o vetor e um índice HNSW com M configurado. As tag vão de 0 a 99 distribuídas uniformemente, então tag < 10 passa cerca de 10% das linhas e tag < 1 cerca de 1%, controlando a seletividade. E cada motor tem ao menos um detalhe de setup capaz de estragar os números em silêncio:
- TOAST no PostgreSQL. Um vetor de 1536 dimensões conta como valor grande e é armazenado fora de linha. Sem definir a coluna como
STORAGE PLAIN, cada comparação de distância paga um fetch extra. É uma linha de DDL; esquecê-la faz o PostgreSQL parecer lento por um motivo que nada tem a ver com sua busca vetorial. - Build incremental vs bulk. No modo incremental o grafo é atualizado a cada INSERT e a tabela já sai pronta para consulta; no bulk, tudo carrega primeiro e o grafo é construído numa passada, muito mais rápido, mas a tabela não responde consultas até terminar. Um motor do conjunto faz os dois, e seu caminho bulk carregou 18 vezes mais linhas por segundo que o próprio incremental. Comparar o bulk de um motor com o incremental de outro compara dois modos de construir índice, não dois motores.
- Full scan silencioso. Qualquer um desses motores pode parar de usar o índice e varrer a tabela. Como o scan devolve resultado exato, porém lento, ele aparece como recall alto e throughput baixo, indistinguível de um índice conservador, a menos que se leia o plano de execução. Um otimizador troca pelo scan quando o
LIMITpassa de cerca de um quarto da tabela; outro faz fallback sem erro nem aviso quando a query pede uma distância diferente daquela para a qual o índice foi construído (construa para cosseno, escreva com o operador L2 e sai um Seq Scan com sort, sem nada avisando). Por isso cada driver rodaEXPLAINe verifica se o nome do índice aparece no plano, emitindoWARNING: vector index NOT usedquando não aparece.
Busca filtrada, o caso que justifica manter vetor no banco
A busca filtrada (WHERE tag < ? ORDER BY distance(v, ?) LIMIT 10) é o cenário que supostamente justifica guardar vetores no banco relacional em vez de num store dedicado, e por isso merece atenção. Filtrar muda o que significa "correto": o top 10 verdadeiro entre as linhas com tag < 10 não é o top 10 geral, então a ground truth precisa ser recalculada por força bruta só sobre as linhas que passam, para cada seletividade. Scorar contra a ground truth do dataset completo dá recall perto de zero em todo motor, e a Percona admite ter feito exatamente esse erro por um tempo.
Há um detalhe de ordem de operações revelador: o HNSW busca por distância primeiro e só depois aplica o WHERE. Ele junta alguns milhares de candidatos, o filtro descarta a maioria e às vezes sobram menos de 10 linhas. Numa rodada, 81 de 200 consultas voltaram incompletas, mesmo com cerca de 99.000 linhas passando o filtro a 10% de seletividade. "10 linhas, quatro erradas" e "seis linhas, todas certas" pontuam ambas 0,6, mas a primeira precisa de busca mais larga e a segunda de iterative scanning; por isso o relatório informa a contagem separada.
Como a comparação é mantida justa
Tudo roda duas vezes. A passada normalizada dá a cada motor CPU, memória e cache idênticos, de modo que diferença no resultado pertença à implementação. A passada tunada deixa cada motor usar o que sua própria documentação recomenda. Um resultado que sobrevive às duas passadas é sobre o motor; um que inverte entre elas é interessante por outro motivo. Os núcleos são fixados um por core físico (irmãos SMT compartilham unidades de execução), nunca misturando P-cores e E-cores em chips híbridos, e a durabilidade é relaxada igualmente em todos, senão o benchmark estaria comparando políticas de fsync sob o rótulo de busca vetorial.
Uma nota de hardware pega gente de surpresa: várias implementações trazem código AVX-512 escrito à mão para a matemática de distância, onde uma instrução faz a aritmética de 16 floats de uma vez. O mesmo índice numa CPU sem AVX-512 é praticamente outro benchmark, e a desaceleração não é a mesma para todos, então nem dá para escalar os números para compensar. Modelo de CPU, feature flags, versões e commits dos motores, IDs de imagem e limites de recurso efetivamente resolvidos entram no manifesto de cada rodada. Sem manifesto, sem relatório.
A honestidade do post se estende aos próprios erros: os primeiros números de ingest mediam 88 linhas por segundo porque o cliente fazia um INSERT por round trip com autocommit ligado; agrupar 500 linhas por transação levou o mesmo motor a 373. E as duas passadas chegaram a compartilhar um diretório de resultados, fazendo a tunada pular tudo o que a normalizada já havia calculado, de modo que os números "tunados" eram quase todos normalizados com outro rótulo. Para quem for avaliar um banco vetorial no Brasil, a lição prática é direta: antes de acreditar em qualquer número, pergunte pela ground truth, pelo par recall/throughput e pela checagem de scan silencioso. Se o benchmark não menciona isso, há motivo para desconfiar.
Fonte: Percona Database Blog
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?