O Problema: Data Lakes Não São Feitos para Consultas Pontuais
Data lakes são ótimos para análises em lote, mas quando você precisa buscar um único registro de usuário—por exemplo, para um agente de IA responder "o que eu estava ouvindo no verão passado?"—mecanismos de consulta tradicionais como Trino ou BigQuery adicionam segundos de overhead. Mesmo com armazenamento em nuvem agora oferecendo latência de dígitos únicos em milissegundos, o gargalo é o planejamento da consulta, não o armazenamento.
O Spotify enfrenta esse desafio em escala de exabytes. Enquanto o Bigtable lida com cargas de trabalho online em petabytes, a maior parte dos dados reside no GCS. A solução não é copiar tudo para um KV store—isso seria proibitivamente caro. Em vez disso, eles construíram um índice externo que torna os arquivos Parquet existentes acessíveis aleatoriamente.
A Ideia Central: Substitua Varreduras por Lookups
O problema fundamental com Parquet é a cadeia de leituras dependentes: buscar o rodapé, analisar metadados de row group, escanear a coluna de chave, localizar páginas. Cada passo requer um round-trip ao armazenamento. RAP elimina essa cadeia usando um índice pré-computado que mapeia cada chave diretamente para arquivo e números de linha.
# Pseudo-código para consulta pontual baseada em RAP
index = load_external_index("user_id")
file_loc, row_num = index.lookup(user_id)
# Agora emita uma única leitura de intervalo para buscar exatamente os bytes necessários
page_data = read_range(file_loc, offset=row_num.page_offset, length=row_num.page_size)
result = decode_page(page_data)
O índice é um multimapa: cada chave pode apontar para várias entradas em diferentes arquivos. O tamanho do índice é aproximadamente 1% dos dados de origem (terabytes de índice para petabytes de dados), o que é gerenciável e distribui bem via hash bucketing.
Otimizações Que Importam
Depois de ter um índice externo, você pode otimizar os arquivos Parquet para consultas pontuais sem quebrar a compatibilidade com análises em lote. As principais otimizações caem em três categorias:
1. Concentrando Dados de Chave
- Ordenar por chave: Garante que linhas da mesma chave sejam contíguas, minimizando páginas a ler.
- Co-agrupamento: Use
ARRAY_AGGpara armazenar todos os valores de uma chave em uma linha—sem necessidade de ordenação. - Particionamento mais grosso: Partições semanais em vez de diárias reduzem o número de arquivos que uma chave abrange.
2. Reduzindo Bytes Lidos
- Uma página por chave: Descarregue páginas em limites de chave, tornando cada página exatamente os dados de uma chave.
- Reinícios de frame ZSTD: Mantenha tamanhos de página convencionais, mas comprima cada chave como um frame separado, permitindo endereçamento direto.
- Alinhamento de armazenamento: Preencha com frames ZSTD skippable para alinhar leituras a limites de bloco.
3. Reduzindo Operações de Leitura
- Blobs/Variants: Armazene campos de consulta pontual como uma única coluna JSON ou Variant—uma leitura por arquivo.
- Intercalação de colunas: Coloque fisicamente colunas para cada chave juntas, permitindo uma única leitura contígua.
- Índices de cobertura: Coloque valores pequenos diretamente no índice, eliminando leituras de armazenamento.
Trade-offs e Limitações
Essas otimizações não são gratuitas. Uma página por chave pode inchar o PageIndex. Reinícios de frame ZSTD forçam codificação PLAIN, que pode reduzir a compressão. Intercalar colunas prejudica varreduras de coluna única adicionando espaço morto. Índices de cobertura aumentam o tamanho do índice. Equipes devem escolher cuidadosamente quais otimizações aplicar com base em seus padrões de acesso.
Próximos Passos
RAP é um padrão promissor, mas requer esforço significativo de engenharia para implementar. Para equipes considerando isso, comece analisando seus padrões de consulta pontual e identificando quais otimizações trazem mais valor. Também explore técnicas complementares como índices secundários para lookups multidimensionais.
Para mais contexto sobre tendências modernas de engenharia de dados, confira esta prévia do Python 3.15 ou aprenda sobre layouts CSS zigzag.

Mergulho Profundo: O Índice Externo
O índice externo é o coração do RAP. Ele é construído lendo rodapés e localizações de páginas, escaneando colunas de chave e escrevendo um mapeamento. Construí-lo é um processo em lote que roda em novos dados à medida que chegam.
# Exemplo: Construindo um fragmento de índice para um novo arquivo Parquet
from rap import IndexBuilder
builder = IndexBuilder(key_column="user_id")
# Leia o rodapé e as localizações de página
metadata = read_parquet_footer(file_path)
# Escaneie a coluna de chave para encontrar row groups
row_groups = scan_key_column(metadata, key_column="user_id")
# Escreva entradas de índice
for row_group in row_groups:
builder.add_entry(key=row_group.key, file=file_path, rows=row_group.rows)
builder.write_fragment("index/user_id/2026-07-01.rap")
O índice é append-only, com fragmentos por execução de pipeline. Esse design evita contenção e permite atualizações incrementais.

Considerações Práticas e Armadilhas
Tamanho e custo do índice: Indexar petabytes produz terabytes de índice. Embora seja mais barato que duplicar dados em um KV store, não é grátis. Considere estratégias de particionamento para manter o tamanho do índice gerenciável.
Trade-offs colunar vs. orientado a linha: Algumas otimizações (como intercalação) tornam o arquivo menos eficiente para varreduras colunares tradicionais. Se seus dados são fortemente usados tanto para análises quanto para consultas pontuais, você pode precisar equilibrar entre os dois.
Maturidade de ferramentas: Esta é uma solução personalizada do Spotify. Alternativas de código aberto podem não oferecer o mesmo nível de otimização. Espere investir na construção de suas próprias ferramentas.
Próximo caminho de aprendizado: Para aprofundar, estude internals do Parquet, compressão ZSTD e estruturas de dados de índice. Também veja como problemas semelhantes são resolvidos em outros sistemas como Apache Druid ou ClickHouse.

Conclusão
RAP mostra que data lakes podem servir cargas de trabalho interativas sem uma camada de serviço separada. Ao colapsar leituras dependentes em um único lookup, ele desbloqueia acesso de baixa latência a exabytes de dados. O principal aprendizado: comece com um índice externo, depois otimize seu layout de arquivo incrementalmente.
Se você está construindo agentes de IA que precisam recuperar contexto de usuário, este é um padrão que vale a pena estudar. Para mais tópicos relacionados, veja a prévia do Python 3.15 ou a técnica de CSS zigzag.