Recursos selecionados para complementar sua leitura

Software Architect
Pós-graduado em arquitetura de software e soluções. Conecto profundidade técnica com resultados de negócio para entregar produtos que as pessoas realmente usam. Também mentoro desenvolvedores e criadores em programas ao vivo, podcasts e iniciativas de comunidade focadas em tecnologia inclusiva.
Checklist de 47 pontos para encontrar bugs, riscos de segurança e problemas de performance antes do lançamento.
Continue explorando tópicos similares

Um guia completo de system design para cache distribuído com Redis e Memcached, cobrindo cache-aside, read-through, write-through, invalidação, TTL, eviction, sharding, replicação, hot keys, preven...

Um guia completo de system design para construir uma plataforma de compartilhamento de corridas lidando com milhões de viagens simultâneas, matching geoespacial em tempo real, precificação dinâmica e predição de ETA em escala global.

Um guia de arquitetura em nível de produção para construir um sistema estilo Instagram com feed personalizado, stories, reels, mensagens, notificações, ranking em tempo real e operação global.
Templates testados em produção, usados por desenvolvedores. Economize semanas de setup no seu próximo projeto.
Consultorias modulares para founders e CTOs fracionados. Você recebe diagnóstico acionável e acompanhamento direto comigo.
2 vagas para consultorias no Q2

Resumo: Um guia prático e senior para desenhar um motor de busca em escala de internet, cobrindo crawler frontier, robots.txt, sitemaps, canonicalização, deduplicação, índice invertido, ranking, snippets, freshness, sharding, multi-região, confiabilidade, abuso, privacidade e trade-offs de entrevista.
Publicado: Fevereiro 2026 Tempo de leitura: 75 minutos Palavras-chave: #SystemDesign #SearchEngine #WebCrawler #Busca #IndiceInvertido #Ranking #SistemasDistribuidos #Escalabilidade #EntrevistaTecnica
Um motor de busca parece simples por fora: uma caixa, uma consulta e uma lista de resultados. Por dentro, o sistema precisa descobrir a web, respeitar publishers, baixar páginas sem sobrecarregar hosts, renderizar JavaScript quando necessário, extrair texto, resolver duplicatas, construir índices comprimidos, ranquear documentos sob pressão adversarial e responder em baixa latência para usuários no mundo todo.
O problema não é "guardar HTML e fazer LIKE". O problema real é alocar crawl budget, decidir o que merece freshness, separar URL de documento, consolidar canonicals, operar shards e réplicas, controlar tail latency, combater spam e continuar servindo quando crawler, indexador ou ranker degradam.
O Google Search Central descreve busca em três estágios: crawling, indexing e serving. Para system design, precisamos abrir esses estágios em subsistemas operacionais: descoberta de URLs, frontier, robots, fetch, render, parser, dedupe, link graph, index builder, segment store, query planner, retrieval, ranking, snippets, safe search, observabilidade e controle de custo.
Este artigo desenha um sistema tipo Google Search/Bing em nível de entrevista senior. Não é clone de arquitetura proprietária. Os números são premissas plausíveis para raciocínio, não afirmações sobre empresas reais.
Escopo: busca web pública, documentos descobertos por links/sitemaps/feeds/submissão, serving global, resultados orgânicos, safe search e freshness seletiva. Fora do escopo principal: leilão de anúncios, busca enterprise privada, geração de respostas por IA, email pessoal, inventário fechado de ecommerce e telemetry proprietária.
| Requisito | Meta | Motivo |
|---|---|---|
| Disponibilidade de busca | 99,99%+ | Busca é superfície primária |
| Latência de query | p50 <= 80ms, p99 <= 250ms | Usuário espera resposta imediata |
| Freshness prioritária | segundos a minutos | Notícias e incidentes envelhecem rápido |
| Recrawl geral | horas a semanas | Crawl budget é finito |
| Politeness | obrigatório por host/site | Não sobrecarregar publishers |
| Consistência | eventual nos resultados | Forte em tudo seria caro demais |
| Segurança | alta | Conteúdo e tráfego são adversariais |
| Custo | budget explícito por tier | Crawl/ranking queimam dinheiro rápido |
| Item | Premissa |
|---|---|
| URLs conhecidas | 300B |
| Documentos canônicos indexados | 50B |
| Fetches por dia | 10B |
| Queries por dia | 20B |
| Multiplicador de pico | 5x |
| Regiões de serving | 6 |
| Resultados orgânicos por query | 10 |
Perguntas de entrevista: é busca web, site search ou enterprise? Quais formatos entram? Safe search é obrigatório? Qual freshness esperada? Personalização entra? Ads entram? Qual QPS? Qual volume de documentos?
Busca precisa de métricas de sistema e métricas de relevância.
Uma SERP rápida com resultados ruins ainda falha.
Uma SERP excelente que chega depois de 2 segundos também falha.
| Métrica | O que mede | Sinal de problema |
|---|---|---|
| Search success rate | usuário encontrou resposta útil | reformulações em sequência |
| Long click rate | clique com permanência | resultado promissor e útil |
| Pogo-sticking | volta rápida para a SERP | snippet enganoso ou página ruim |
| Query abandonment | saída sem clique/resposta | baixa relevância ou latência |
| Empty result rate | consultas sem resposta | cobertura ruim ou filtros agressivos |
| Fresh result coverage | documentos recentes quando necessário | notícias atrasadas |
| Safe search precision | filtro acerta conteúdo sensível | overblocking/underblocking |
| Crawl useful yield | fetch que vira valor de busca | frontier desperdiçando budget |
Ads não entram no core.
Um leilão de anúncios tem latência, fairness, billing e compliance próprios.
Busca local também pode ser um vertical separado.
Ela depende de inventário de negócios, geocoding, horário de funcionamento e ranking geoespacial.
Busca por imagens e vídeos exige embeddings multimodais, thumbnails, transcodificação e políticas próprias.
O core deste guia é web search textual com capacidade de misturar verticais quando a query pede.
Resultados de busca aceitam consistência eventual.
Se uma página nova aparece em uma região antes da outra, isso é tolerável na maioria dos casos.
Configuração de política não deve ser eventual sem controle.
Remoções legais, noindex, malware, safe search e ponteiros de manifest ativo exigem semântica mais forte.
Essa separação evita pagar custo de coordenação global em tudo.
Fetch/day = 10B URLs * 100KB comprimidos ~= 1PB/dia
Bandwidth médio = 1PB / 86.400 ~= 11,6GB/s
Pico 3x de crawl ~= 35GB/s
QPS médio = 20B / 86.400 ~= 231K queries/s
QPS pico 5x ~= 1,16M queries/s
Texto indexado = 50B docs * 10KB ~= 500TB
Índice invertido primário ~= 20% a 40% do texto normalizado
Postings primários ~= 100TB a 200TB
Com posições, campos, doc values, réplicas e tiers: múltiplos PB
QPS pico: 1,16M
Candidatos por query: 10.000
Scores/s: 11,6B
Conclusão: ranking precisa ser multi-estágio. O sistema recupera candidatos baratos, poda por score/qualidade, ranqueia centenas, re-ranqueia dezenas e só então gera SERP.
URLs conhecidas: 300B
Fetches/dia: 10B
Recrawl uniforme: 30 dias
Recrawl uniforme é ruim. Home pages, notícias e páginas de alta mudança merecem prioridade; páginas estáveis, duplicadas ou fracas podem esperar semanas.
O índice não é o único custo de storage.
Raw fetch comprimido: 1PB/dia
Retencao hot de 7 dias: ~7PB
Texto extraido: 10B fetches * 10KB ~= 100TB/dia logico
Metadados e features: dezenas de TB/dia
Query logs em pico: centenas de TB/dia antes de sampling/agregacao
Raw pages podem ir para retenção curta.
Documentos indexáveis, features e snapshots de segmentos precisam de retenção maior.
Logs de query exigem política de privacidade, agregação e sampling.
Um budget p99 plausível:
Total p99: 250ms
Edge/auth: 15ms
Query planning: 10ms
Shard retrieval: 90ms
Merge global: 15ms
Ranking/re-ranking: 60ms
Snippets/safety: 40ms
Assembly: 20ms
O gargalo tende a ser fanout para shards e re-ranking.
Por isso a arquitetura precisa de deadlines.
Cada etapa deve saber quanto tempo ainda resta.
Três conclusões caem direto dos cálculos:
Escalar máquinas sem mudar essas três decisões só aumenta a conta.
Princípios: separar crawl/index/serving; tratar URL e documento como identidades diferentes; politeness é invariante; serving não depende do crawler; segmentos são imutáveis; manifest versionado publica índice; freshness usa tiers; ranking é pipeline.
type SearchVertical = "web" | "news" | "images" | "videos";
interface SearchRequest {
query: string;
vertical?: SearchVertical;
locale?: string;
language?: string;
country?: string;
device?: "desktop" | "mobile" | "tablet";
safeSearch?: "off" | "moderate" | "strict";
pageSize?: number;
cursor?: string;
userContext?: {
signedIn: boolean;
privacyMode: "standard" | "limited" | "anonymous";
};
}
interface SearchResponse {
queryId: string;
correctedQuery?: string;
rewrittenQuery?: string;
results: Array<{
rank: number;
url: string;
displayUrl: string;
title: string;
snippet: string;
lastCrawledAt: string;
resultType: "organic" | "news" | "video" | "image" | "direct_answer";
safetyLabels: string[];
}>;
nextCursor?: string;
latencyMs: number;
}
interface FrontierLeaseRequest {
workerId: string;
region: string;
maxUrls: number;
renderCapacity: "none" | "limited" | "full";
}
interface FrontierLeaseItem {
urlId: string;
normalizedUrl: string;
hostKey: string;
crawlPriority: number;
politenessDeadline: string;
renderHint: "skip" | "maybe" | "required";
}
interface FetchReport {
leaseId: string;
urlId: string;
finalUrl: string;
statusCode: number;
fetchedAt: string;
outcome: "fetched" | "not_modified" | "redirected" | "disallowed" | "timeout" | "server_error" | "too_large";
rawObjectKey?: string;
extractedObjectKey?: string;
discoveredLinks?: string[];
}
APIs públicas têm semântica estável. APIs internas precisam de versão, idempotência, query IDs, lease IDs e tracing. Submeter URL não significa indexar; significa aceitar a URL para avaliação de discovery.
interface UrlSubmitRequest {
url: string;
source: "site_owner" | "api" | "feed" | "manual";
sitemapUrl?: string;
declaredLastModified?: string;
}
interface UrlSubmitResponse {
accepted: boolean;
normalizedUrl: string;
reason?: "invalid_url" | "blocked_host" | "quota_exceeded" | "already_known";
estimatedCrawlTier?: "high" | "normal" | "low";
}
Essa API deve exigir ownership verification para canais privilegiados.
Sem isso, vira vetor de spam e DDoS indireto contra o crawler.
interface PlannedQuery {
queryId: string;
originalQuery: string;
normalizedTerms: string[];
phrases: string[][];
requiredTerms: string[];
optionalTerms: string[];
filters: {
language?: string;
country?: string;
safeSearch: "off" | "moderate" | "strict";
freshnessWindow?: "hour" | "day" | "week" | "any";
};
retrievalPlan: Array<{
indexTier: "fresh" | "main" | "archive";
shardFanout: number;
maxCandidatesPerShard: number;
}>;
}
O planner não deve expor detalhes proprietários para o cliente.
Ele é contrato interno entre edge, retrieval e ranking.
queryId.Essas regras parecem burocracia.
Em produção, elas tornam rollback, auditoria e debug possíveis.
CREATE TABLE url_record (
url_id BIGINT PRIMARY KEY,
normalized_url TEXT NOT NULL,
url_hash BINARY(16) NOT NULL,
host_key TEXT NOT NULL,
discovered_at TIMESTAMP NOT NULL,
discovery_source TEXT NOT NULL,
canonical_doc_id BIGINT,
crawl_priority DOUBLE PRECISION NOT NULL,
next_crawl_at TIMESTAMP,
last_crawl_at TIMESTAMP,
last_status_code INTEGER,
robots_state TEXT NOT NULL,
render_hint TEXT NOT NULL,
is_active BOOLEAN NOT NULL DEFAULT TRUE
);
CREATE INDEX idx_url_host_next ON url_record (host_key, next_crawl_at);
CREATE UNIQUE INDEX idx_url_hash ON url_record (url_hash);
CREATE TABLE document_record (
doc_id BIGINT PRIMARY KEY,
canonical_url TEXT NOT NULL,
canonical_url_id BIGINT NOT NULL,
content_hash BINARY(16) NOT NULL,
simhash BIGINT NOT NULL,
title TEXT,
language TEXT,
content_type TEXT NOT NULL,
first_indexed_at TIMESTAMP NOT NULL,
last_indexed_at TIMESTAMP NOT NULL,
last_crawled_at TIMESTAMP NOT NULL,
quality_score DOUBLE PRECISION NOT NULL,
spam_score DOUBLE PRECISION NOT NULL,
safe_search_level TEXT NOT NULL,
indexable BOOLEAN NOT NULL
);
CREATE TABLE link_edge (
source_doc_id BIGINT NOT NULL,
target_url_id BIGINT NOT NULL,
target_doc_id BIGINT,
anchor_text_hash BINARY(16),
rel_nofollow BOOLEAN NOT NULL,
discovered_at TIMESTAMP NOT NULL,
last_seen_at TIMESTAMP NOT NULL,
PRIMARY KEY (source_doc_id, target_url_id)
);
CREATE TABLE index_segment (
segment_id TEXT PRIMARY KEY,
index_tier TEXT NOT NULL,
shard_id INTEGER NOT NULL,
generation BIGINT NOT NULL,
doc_count BIGINT NOT NULL,
deleted_doc_count BIGINT NOT NULL,
object_key TEXT NOT NULL,
checksum TEXT NOT NULL,
published_at TIMESTAMP
);
| Dado | Acesso | Storage |
|---|---|---|
| Frontier | lookup por host/tempo | KV/wide-column |
| Página bruta | write once/read pipeline | object storage |
| Documento extraído | lookup por doc ID | wide-column |
| Link graph | jobs batch | arquivos distribuídos |
| Segmentos | leitura otimizada | SSD local + object backup |
| Query logs | append-only | stream + warehouse |
Lição do Bigtable: registros esparsos, versionados e em escala enorme combinam bem com chaves por URL/reversed URL. Mas chaves por host podem criar ranges quentes; use salting, particionamento e filas separadas.
CREATE TABLE crawl_attempt (
attempt_id BIGINT PRIMARY KEY,
url_id BIGINT NOT NULL,
lease_id TEXT NOT NULL,
worker_id TEXT NOT NULL,
started_at TIMESTAMP NOT NULL,
finished_at TIMESTAMP,
status_code INTEGER,
outcome TEXT NOT NULL,
fetch_duration_ms INTEGER,
render_duration_ms INTEGER,
bytes_downloaded BIGINT,
raw_object_key TEXT,
extracted_object_key TEXT
);
CREATE INDEX idx_crawl_attempt_url_time
ON crawl_attempt (url_id, started_at DESC);
Separar url_record de crawl_attempt evita sobrescrever histórico útil.
Histórico de tentativas alimenta backoff, politeness, detecção de hosts instáveis e priorização de recrawl.
CREATE TABLE canonical_cluster (
cluster_id BIGINT PRIMARY KEY,
canonical_doc_id BIGINT NOT NULL,
representative_url TEXT NOT NULL,
cluster_hash BINARY(16) NOT NULL,
member_count INTEGER NOT NULL,
updated_at TIMESTAMP NOT NULL
);
CREATE TABLE canonical_cluster_member (
cluster_id BIGINT NOT NULL,
url_id BIGINT NOT NULL,
doc_id BIGINT,
match_type TEXT NOT NULL,
confidence DOUBLE PRECISION NOT NULL,
PRIMARY KEY (cluster_id, url_id)
);
Cluster canônico resolve o fato de que várias URLs podem representar o mesmo documento.
Isso reduz bloat no índice e melhora diversidade na SERP.
CREATE TABLE query_log (
query_id TEXT PRIMARY KEY,
normalized_query TEXT NOT NULL,
locale TEXT,
country TEXT,
device TEXT,
safe_search TEXT NOT NULL,
served_at TIMESTAMP NOT NULL,
latency_ms INTEGER NOT NULL,
result_count INTEGER NOT NULL,
index_manifest_id TEXT NOT NULL,
cache_hit BOOLEAN NOT NULL
);
CREATE TABLE result_impression (
query_id TEXT NOT NULL,
rank INTEGER NOT NULL,
doc_id BIGINT NOT NULL,
score DOUBLE PRECISION NOT NULL,
clicked BOOLEAN,
dwell_time_ms INTEGER,
PRIMARY KEY (query_id, rank)
);
Esses logs são poderosos e perigosos.
Eles alimentam avaliação, ranking, debugging e detecção de regressão.
Também podem expor intenção sensível do usuário.
Retenção, acesso e agregação precisam ser tratados como parte do design.
Use chaves diferentes para problemas diferentes.
| Entidade | Chave útil | Cuidado |
|---|---|---|
| URL | hash da URL normalizada | colisão e over-normalization |
| Host | registrable domain + host | hosts gigantes criam hot spots |
| Documento | doc_id interno | precisa sobreviver a reindex |
| Segmento | tier + shard + generation | manifest decide visibilidade |
| Query | hash normalizado + locale | privacidade e ambiguidade |
Um erro comum é tentar usar URL como identidade única de tudo.
URL é endereço.
Documento é conteúdo.
Cluster é equivalência.
Segmento é representação de serving.
Crawler frontier decide o que baixar, quando, por quem e com qual custo. Não é uma fila FIFO.
Fontes: links, sitemaps, feeds, submissão, histórico de recrawl e feeds confiáveis. Riscos: spam, espaços infinitos de URL, sitemaps mentirosos, parâmetros de tracking e retry storms.
crawl_priority =
0.35 * host_quality
+ 0.20 * url_importance
+ 0.15 * expected_change_rate
+ 0.10 * freshness_demand
+ 0.10 * sitemap_confidence
+ 0.05 * historical_fetch_success
- 0.20 * spam_risk
- 0.10 * duplicate_risk
- 0.10 * crawl_cost
Use filas por tier: fresh, recrawl normal, exploração, long-tail, retry e render-required. Cada fila precisa de budget. Sem budget, spam ou notícias podem tomar o sistema inteiro.
| Fonte | Valor | Risco |
|---|---|---|
| Links extraídos | reflete a web real | spam e loops |
| Sitemaps | intenção do publisher | lastmod falso |
| Feeds RSS/Atom | freshness | cobertura parcial |
| URL submit | descoberta direta | abuso e quota gaming |
| Histórico de recrawl | mudança observada | viés para passado |
| Feeds confiáveis | notícias e emergências | dependência externa |
Workers não devem remover URLs da fila de forma definitiva.
Eles recebem leases.
Se o worker morre, o lease expira.
Depois disso, a frontier pode reagendar a URL.
Esse modelo evita trabalho perdido e reduz duplicidade.
| Falha | Efeito | Mitigação |
|---|---|---|
| Explosão de URLs duplicadas | gasto inútil | normalização e filtros |
| Host hotspot | sobrecarga externa | token bucket por host |
| Retry storm | custo e dano | backoff com jitter |
| Sitemap ruim | páginas fracas | score de confiança |
| Calendários infinitos | espaço sem fim | classificador de padrões |
Frontier madura não pergunta só "posso baixar?".
Ela pergunta "vale baixar agora?".
Robots e politeness são requisitos de corretude. Um crawler ruim causa dano fora da sua empresa.
Política comum: robots 2xx é parseado e cacheado; 404 significa ausência de arquivo; 5xx reduz ou pausa crawl; timeout gera retry cauteloso; arquivo gigante é limitado. Robots não é segurança, é protocolo de crawling.
Sitemaps ajudam discovery, mas não garantem crawl, indexação ou ranking. Avalie confiança: lastmod bate com mudanças reais? URLs retornam 200? Há muitos 404? Todo lastmod é "agora"? Há duplicatas demais?
Politeness usa token bucket por host e às vezes por domínio registrável. Taxa cai com 5xx, timeout e latência alta; sobe devagar com saúde comprovada.
| Resposta robots.txt | Comportamento |
|---|---|
| 200 | parsear e cachear |
| 404 | tratar como sem robots |
| 401/403 | reduzir confiança e pausar caminhos sensíveis |
| 5xx | desacelerar ou pausar host |
| timeout | retry cauteloso |
| arquivo gigante | limitar leitura e registrar anomalia |
Robots precisa ser cacheado com TTL.
TTL curto aumenta fetches de robots.
TTL longo pode respeitar regras antigas por tempo demais.
O sistema deve permitir invalidação para sites importantes e mudanças verificadas.
Um sitemap pode apontar milhões de URLs.
Isso não significa que todas merecem crawl imediato.
Use sitemaps para priorizar descoberta, não para abrir mão de ranking de crawl.
Sinais bons:
lastmod consistente com mudanças reais;Sinais ruins:
lastmod igual ao horário atual;host_budget =
base_rate
* health_factor
* trust_factor
* demand_factor
* change_rate_factor
health_factor cai com 5xx, timeout e latência.
trust_factor cai com spam e sitemap ruim.
demand_factor sobe quando usuários procuram conteúdo daquele host.
change_rate_factor sobe quando o conteúdo muda de verdade.
Fetch envolve DNS, TLS, redirects, conditional GET, content-type, limites de bytes, rendering seletivo, parsing e relatório idempotente.
Renderizar tudo é caro. Renderize quando HTML tem pouco texto, host historicamente depende de JS, página é valiosa ou dados estruturados aparecem após JS. Pule quando a página é baixa prioridade, excede CPU/rede, bloqueia recursos ou parece abusiva.
Parser emite: título, descrição, texto principal, boilerplate score, idioma, canonical tag, links, media refs, robots meta, content hash, simhash, structured data e features de segurança.
| Recurso | Limite típico |
|---|---|
| Wall time | 5s a 10s |
| CPU por página | limitado por tier |
| Requests de subrecursos | limitado |
| Bytes carregados | limitado |
| Execução JS | sandbox |
| APIs sensíveis | bloqueadas |
Render farm precisa ser isolada.
Páginas não confiáveis rodam JavaScript arbitrário.
Use sandbox, containers, rede restrita, patching frequente e descarte de estado entre páginas.
HTML real é quebrado.
Boilerplate muda por site.
Datas podem ser ambíguas.
Texto visível pode divergir de DOM.
Por isso parser deve produzir score de confiança, não só campos.
Quando confiança é baixa em página importante, renderize ou mande para pipeline especializado.
A web duplica tudo: HTTP/HTTPS, www/bare, tracking params, sessão, print pages, mobile, AMP, cópias sindicadas e variações locais.
Normalização: lowercase em scheme/host, remover portas padrão, normalizar encoding, ordenar parâmetros seguros, remover tracking conhecido, resolver dot segments e preservar case de path quando necessário.
Sinais: redirect forte, rel=canonical forte porém abusável, sitemap médio, links internos médio, hash exato forte, simhash/minhash médio-forte, idioma/região contextual.
Escolha canonical por HTTPS, status estável, conteúdo completo, links internos/externos, segurança, locale e hints consistentes. Canonicalização não é deletar alternates; variantes podem servir para país, idioma, device ou query exata.
Regras úteis:
. e .. no path;Over-normalization causa bug difícil.
/Produto e /produto podem ser páginas diferentes.
?page=2 não é tracking.
?sort=price pode alterar conteúdo.
Camadas:
Páginas duplicadas não são sempre lixo.
Uma versão mobile, uma versão traduzida e uma versão regional podem ser úteis em contextos diferentes.
O cluster guarda alternates para serving contextual.
| Critério | Preferência |
|---|---|
| Protocolo | HTTPS |
| Status | 200 estável |
| Conteúdo | versão mais completa |
| Canonical hint | consistente com outras pistas |
| Links | maior suporte interno/externo |
| Segurança | menor risco |
| Locale | compatível com usuário/query |
Links são discovery e sinal de importância. O paper original do Google explorava estrutura de hipertexto para melhorar busca; a ideia ainda é central, com muito mais defesa contra abuso.
Edge bom tem source doc, target URL/doc, anchor, texto ao redor, rel, seção da página, posição, first/last seen e internal/external. Qualidade combina reputação de host, originalidade, profundidade, links, histórico, risco de malware/phishing, spam, excesso de afiliados e qualidade do cluster.
Link analysis costuma ser offline/nearline porque o grafo é enorme, iterativo e sensível a filtros de spam. Use snapshots reprodutíveis e sinais frescos locais para páginas novas.
Não armazene só source e target.
Guarde:
rel=nofollow, ugc e sponsored;Anchor text é forte para queries navegacionais.
Também é vetor clássico de manipulação.
Por isso o ranking deve descontar links de baixa confiança.
Sinais úteis:
| Sinal | Uso |
|---|---|
| reputação de host | priorização e ranking |
| originalidade | combate scraping |
| profundidade de conteúdo | evita thin pages |
| histórico de atualização | freshness |
| link authority | importância |
| malware/phishing | bloqueio |
| spam score | demotion |
| excesso de ads/affiliate | qualidade |
Qualidade estática não substitui relevância lexical.
Uma página excelente sobre assunto errado não deve rankear.
Uma página lexicalmente perfeita com baixa confiança também não deve subir sem limites.
Ranking bom combina as duas dimensões.
Índice invertido mapeia termo para documentos. Em busca web, postings carregam campos, posições, frequências e payloads.
termo: "busca"
df: 120.000.000
postings:
doc 101, tf=4, fields=title/body, pos=[1, 18, 44, 90]
doc 140, tf=1, fields=body, pos=[55]
Campos úteis: title, headings, body, anchor text, URL tokens, structured data, alt text, idioma e datas. Compressão usa delta docID, blocos, skip pointers, variable byte, bit packing, frame-of-reference e técnicas SIMD-friendly. Compressão economiza disco e melhora cache/memória.
Segmentos devem ser imutáveis. Updates viram novo segmento + delete bitset. Publicação é troca de manifest. Rollback é voltar o ponteiro para último manifest bom.
Tiers: real-time em segundos, fresh em minutos, daily em horas, main em dias, archive frio. Query "terremoto hoje" consulta fresh + main; query "binary search" pode ficar no main.
| Modo | Uso | Trade-off |
|---|---|---|
| Full rebuild | mudança grande de analyzer ou campo | caro e lento |
| Batch incremental | atualizações normais de crawl | precisa merge policy |
| Micro-index real-time | documentos urgentes | caro por doc |
| Backfill paralelo | novo campo ou feature | aumenta complexidade de serving |
Full rebuild é inevitável em mudanças estruturais.
Exemplo: trocar tokenizer, adicionar posições por campo ou mudar docID layout.
Incremental é caminho normal para o dia a dia.
Micro-index existe para freshness.
Backfill evita esperar o próximo rebuild global.
HTML -> texto visivel -> idioma -> tokenizacao -> normalizacao
-> stemming/lemmatizacao seletiva -> campos -> postings
Decisões importantes:
Analyzer versionado é obrigatório.
Sem isso, um segmento antigo e um segmento novo podem interpretar termos de formas diferentes.
Deletes usam bitsets.
Updates viram nova versão de documento.
Merges compactam deletes, reduzem número de segmentos e melhoram locality.
Merge agressivo reduz latência de query, mas consome CPU/IO.
Merge preguiçoso melhora velocidade de publicação, mas aumenta fanout interno.
Resposta prática: tiered merge com budget explícito e feedback de latência.
Query planner normaliza, entende intenção, aplica rewrites e decide quais shards/tier consultar.
Tipos: navegacional, informacional, transacional, fresh, local, frase exata, site-scoped e filtros. Retrieval básico pega postings, intersecta/une, calcula BM25-like, aplica qualidade/freshness, mantém topK por shard e faz merge global. Retrieval otimizado usa skip, champion lists, block max WAND, early termination, tiered indexes e cache de blocos.
Ranking usa relevância lexical, qualidade, freshness, locale, safe search, popularidade agregada, estrutura de conteúdo, diversidade e sinais anti-spam. Stage 1 roda em milhares, stage 2 em centenas, stage 3 em dezenas. Blending injeta news, images, videos, local pack ou direct answer só quando a intenção justifica.
Tail latency vem do fanout: se uma query toca 1.000 shards, o shard lento domina p99. Mitigue com réplicas, hedged requests, deadlines, partial results, cache e shard splitting.
| Tipo | Exemplo | Estratégia |
|---|---|---|
| Navegacional | github login | favorecer entidade/site oficial |
| Informacional | como funciona tcp | relevância ampla e qualidade |
| Fresh | resultado eleição hoje | fanout para fresh/news |
| Local | farmacia perto de mim | vertical local |
| Frase exata | "distributed transactions" | posições obrigatórias |
| Site scoped | site:example.com pricing | filtro por host |
| Código | TypeError cannot read property | preservar tokens raros |
Query de código não deve receber stemming agressivo.
Query médica não deve ser corrigida de forma ousada.
Query navegacional não precisa explorar muitos documentos.
O planner precisa carregar intenção para reduzir custo e erro.
1. buscar dicionario dos termos
2. ordenar termos por seletividade
3. ler blocos de postings comprimidos
4. aplicar skip/block max
5. calcular score parcial
6. podar candidatos impossiveis
7. manter topK por shard
8. retornar score bounds para merge
Block max WAND e técnicas similares evitam pontuar documentos que não podem entrar no topK.
Isso é diferença entre busca teórica e busca de produção.
| Família | Exemplos |
|---|---|
| Lexical | BM25, frase, proximidade, campo |
| Qualidade | autoridade, originalidade, reputação |
| Freshness | publish time, crawl time, QDF |
| Contexto | idioma, país, device, safe mode |
| Popularidade | cliques agregados, com defesa anti-spam |
| Estrutura | title, headings, schema.org |
| Segurança | adult, malware, phishing |
| Diversidade | host, tópico, formato |
Ranking precisa ser auditável.
Quando um resultado ruim sobe, o time precisa saber se foi lexical, freshness, link signal, modelo, spam bypass ou blending.
Por isso cada estágio deve logar scores intermediários em amostras controladas.
SERP boa não é só ordenação. Snippet ajuda o usuário a escolher resultado.
Snippet deve preferir passagem com termos da query, preservar frases, evitar boilerplate, respeitar no-snippet, filtrar texto inseguro e lidar com idiomas. Correção usa dicionário do corpus, logs agregados, distância de edição, teclado, fonética, entidades e contexto. Seja conservador com nomes, código, termos médicos, jurídicos e queries entre aspas.
Sugestões precisam de filtro contra adulto em modo estrito, assédio, dados pessoais, malware, ilegalidade e campanhas de spam. Não exponha trending query crua.
Um snippet bom não é sempre a meta description.
Meta description pode ser genérica, desatualizada ou manipulada.
Pipeline prático:
nosnippet;Correção agressiva melhora typos comuns.
Também destrói intenção rara.
Exemplos de cuidado:
Quando confiança é média, mostre resultados para a correção e permita voltar à query original.
Quando confiança é baixa, apenas sugira.
Freshness é seletiva. A maioria das páginas não precisa entrar em segundos; algumas queries precisam.
Sinais: feeds, sitemap lastmod, RSS/Atom, histórico de mudança, spikes de query, publishers confiáveis e entidades/eventos. Fresh index é pequeno, rápido, caro por doc, tem retenção curta e merge posterior para main.
Falha: se fresh indexing cai, sirva main index com métrica de degradação. Não derrube busca inteira por perda de freshness.
Nem toda query recente merece resultado novo.
Classificador QDF pode usar:
Exemplos frescos:
| Query | Motivo |
|---|---|
terremoto agora | evento em andamento |
resultado do jogo hoje | placar muda rápido |
queda aws status | incidente técnico |
eleições apuração | atualização contínua |
Exemplos evergreen:
| Query | Motivo |
|---|---|
algoritmo de busca binária | conhecimento estável |
o que é tcp | conceito estável |
história de roma | freshness pouco relevante |
Freshness é feature condicional, não boost global.
Boost global favorece páginas novas ruins contra páginas antigas excelentes.
Busca é adversarial. Qualquer sinal que gera valor será atacado.
| Abuso | Sinal | Ação |
|---|---|---|
| Keyword stuffing | repetição anormal | ignorar/demotar |
| Cloaking | crawler vs browser diverge | demotar/remover |
| Link farm | grafo artificial | descontar links |
| Scraping | duplicata fraca | canonicalizar/demotar |
| Malware | sandbox/feeds | bloquear/avisar |
| Phishing | impersonação | bloquear/avisar |
| Adulto | classificador | safe search |
| Doorway | muitas páginas finas | cluster/demote |
Safe search precisa ser configurável, auditável, regionalmente consciente e medido por falso positivo/negativo. Use defesa em profundidade: crawl, index, rank, serving, review e feedback de publisher.
| Camada | Controle |
|---|---|
| Discovery | bloquear padrões infinitos e hosts abusivos |
| Crawl | reduzir budget de hosts suspeitos |
| Parser | detectar cloaking e payload suspeito |
| Index | impedir docs inseguros ou noindex |
| Ranking | demotar spam e baixa qualidade |
| Serving | aplicar safe search e avisos |
| Review | gerar rótulos para treino |
Aplicar só no ranking é tarde.
Spam já consumiu crawl, storage e indexação.
Aplicar só no crawler também é insuficiente.
Páginas legítimas podem ser comprometidas depois.
Safe search precisa separar categorias.
Adulto explícito, violência gráfica, malware, phishing e conteúdo ilegal não são a mesma classe.
Cada classe tem política, threshold e tratamento regional.
Também precisa haver auditoria para falsos positivos.
Overblocking em saúde, educação ou notícias pode degradar qualidade e confiança.
Sharding por doc ID/hash balanceia storage, mas query toca muitos shards. Term sharding reduz leitura por termo, mas complica multi-termo. Host sharding ajuda crawler, mas é ruim para serving. Em web search, doc sharding + otimização de fanout costuma ser escolha prática.
Cache: result cache por query/locale/safe mode; query plan cache; postings block cache; dictionary cache; feature cache; snippet cache. Cache ajuda hot queries, mas a base precisa sobreviver a misses.
Serving deve ser active-active regional. Índices publicados replicam para regiões. Control plane guarda manifest ativo, configs de experimento, políticas, remoções legais e quotas globais. Lição do Spanner: use consistência forte onde correção importa; use eventual onde escala/latência importam.
| Estratégia | Vantagem | Custo |
|---|---|---|
| Por docID/hash | balanceia armazenamento | query toca muitos shards |
| Por termo | menos shards por termo | multi-termo complexo |
| Por tier | custo/freshness controlado | planner mais difícil |
| Por host | bom para crawl/storage | ruim para serving |
Em web search, doc sharding costuma vencer para serving geral.
O sistema compensa fanout com pruning, cache, réplicas e deadlines.
Consistent hashing reduz movimentação quando adicionamos shards.
Mas ele não resolve tudo.
Use virtual nodes, split manual de hosts gigantes, detecção de hot shard e routing map versionado.
Separar shard lógico de partição física ajuda rebalanceamento.
Query serving deve ficar perto do usuário.
Crawl pode ser distribuído, mas politeness precisa de visão global por host.
Index build pode ser centralizado ou regional.
O ponto essencial: cada região precisa servir o último manifest bom sem depender do crawler ou do builder.
Falhas esperadas:
Ordem de degradação em serving: pular re-ranker caro, simplificar snippets, reduzir fanout de tiers não críticos, usar feature cache, desativar vertical blending incerto, servir lexical+static rank e aceitar partial results com logging.
Publish seguro: checksum, regressão de queries, comparação de overlap, latência, safety, deltas de doc count, deleções, canary por região e rollback pointer.
Evite retry amplification: deadlines, retries limitados, jitter, token buckets, circuit breakers, hedging só em leitura com cancelamento e load shedding.
| Degradação | Impacto | Quando usar |
|---|---|---|
| pular re-ranker L3 | qualidade menor | p99 alto |
| snippet simples | SERP menos útil | snippet service lento |
| reduzir fresh fanout | resultados menos atuais | fresh tier degradado |
| desativar blending incerto | SERP mais simples | vertical instável |
| usar feature cache stale | ranking menos preciso | feature store lenta |
| partial results | recall menor | shard timeout |
Degradação precisa ser intencional.
Sem ordem definida, cada serviço falha de um jeito e a SERP vira loteria.
Antes de ativar manifest:
Rollback de índice deve ser troca de ponteiro, não rebuild emergencial.
Crawler precisa bloquear IPs privados, limitar bytes/CPU, sandboxar renderers, isolar parsers, lidar com decompression bombs, redirects infinitos, DNS rebinding, PDFs maliciosos e content-type confuso.
Query logs são dados sensíveis: podem revelar saúde, localização, intenção e identidade. Use retenção curta, agregação, pseudonimização, controle de acesso, auditoria e revisão de privacidade para features.
SLOs: query availability 99,99%; p99 <= 250ms; p99.9 <= 800ms; empty result rate por locale; shard timeout; fresh publish latency; robots compliance; bad manifest rollback em minutos. Métricas de qualidade: long click, reformulation, abandonment, julgamento humano, spam no topK, safe search false positive/negative e satisfação com snippets.
| Plano | Sinais principais |
|---|---|
| Crawl | fetch rate, status codes, politeness delay, duplicate rate |
| Index | segment build time, publish latency, corrupt segments, delete spikes |
| Serving | p50/p99, shard timeout, cache hit, fanout, empty results |
| Ranking | feature latency, model version, score distribution, regressions |
| Safety | blocked docs, false positives, appeals, malware hits |
Cada query amostrada deve permitir reconstruir:
Sem isso, debugging vira tentativa de reproduzir um sistema probabilístico depois que o estado mudou.
Alertas úteis combinam sintoma e impacto.
CPU alta sozinho é ruído.
p99 alto + shard timeout + queda de long click é incidente real.
Para busca, alertas devem cruzar infraestrutura e qualidade.
Maiores custos: bandwidth de crawl, render CPU, parser CPU, object storage, index storage, query fanout, inferência de ranking e analytics.
| Alavanca | Economiza | Risco |
|---|---|---|
| Crawl budget | rede/CPU | corpus stale |
| Conditional GET | bandwidth | suporte varia |
| Render seletivo | CPU | perder JS content |
| Dedupe cedo | storage/serving | canonical errado |
| Tiered indexes | CPU | planner complexo |
| Dynamic pruning | CPU | recall baixo |
| Feature cache | latência | feature stale |
| Log sampling | analytics | debug pior |
Métrica útil:
useful_index_yield =
docs canonicos indexados com impressoes
/ fetch attempts totais
Se um host consome 2% do crawl budget e gera 0,001% de impressões úteis, revise.
Render é uma das formas mais fáceis de gastar CPU.
Política prática:
Se render não aumenta texto útil, links úteis ou dados estruturados, ele deve perder prioridade.
Logs são caros e sensíveis.
Use:
Economizar log sem perder debug exige amostragem inteligente, não corte cego.
Fluxo forte: esclarecer escopo, declarar escala, separar crawl/index/serving, explicar frontier/politeness, canonicalização, índice invertido, query fanout, ranking, freshness, sharding, cache, abuso, privacidade e rollback.
Frases senior: "URL e documento são identidades diferentes"; "politeness é corretude"; "segmentos imutáveis publicados por manifest"; "freshness usa tiers"; "ranking é staged"; "tail latency vem do fanout"; "crawl budget é alocado, não só escalado".
Atalho de whiteboard:
Crawl Plane -> Indexing Plane -> Serving Plane
Policy/Quality atravessa os três
Um motor de busca web é um sistema distribuído sob pressão de escala, custo, abuso e produto. A solução não é um banco, uma fila ou um modelo. É uma cadeia de decisões controladas: quais URLs merecem crawl, quais páginas podem ser buscadas, qual documento é canônico, qual conteúdo entra no índice, quais shards respondem, quais candidatos recebem ranking caro e quais resultados são seguros.
O desenho senior separa planos, usa frontier com budget, respeita robots, faz dedupe antes de inflar o índice, publica segmentos imutáveis, opera tiers de freshness, ranqueia em estágios, observa sistema e relevância juntos, protege logs e degrada sem apagar a busca.
Essa é a diferença entre "busca textual" e um motor de busca real.
| Componente | Responsabilidade |
|---|---|
| URL Discovery | achar URLs candidatas |
| Frontier | agendar crawl budget |
| Robots Service | aplicar permissões |
| Fetch Service | baixar com segurança |
| Render Farm | renderizar JS seletivamente |
| Parser | extrair conteúdo e links |
| Dedupe | formar clusters canônicos |
| Link Graph | gerar sinais de autoridade |
| Index Builder | escrever segmentos comprimidos |
| Query Planner | planejar retrieval |
| Shard Retrieval | buscar candidatos |
| Ranking | ordenar resultados |
| Snippet | resumir resultados |
| Safety | filtrar conteúdo inseguro |
| Decisão | Opção A | Opção B | Resposta prática |
|---|---|---|---|
| Crawl | agressivo | conservador | adaptativo por host |
| Render | tudo | nada | seletivo |
| Index | rebuild | incremental | ambos |
| Freshness | tempo real | batch | tiers |
| Ranking | modelo pesado | lexical simples | staged |
| Consistência | forte em tudo | eventual em tudo | forte no controle, eventual no resultado |
Budget p99 250ms:
Edge/auth: 15ms
Query planning: 10ms
Shard retrieval: 90ms
Merge: 15ms
Ranking: 60ms
Snippets/safety: 40ms
Assembly: 20ms
Uma frase: motor de busca é ingestão crawl-budgeted e policy-aware alimentando índices comprimidos imutáveis, servidos por fanout com deadline e ranking em estágios sob pressão constante de abuso.