Do Batch ao Interativo: A Revolução do Data Lake
O data lake sempre foi o lar da análise em lote—processando petabytes de dados com queries SQL massivas para relatórios e pipelines de ML. Mas com o avanço dos agentes de IA e da personalização em tempo real, surge a necessidade de acessar registros individuais (ex: histórico de audição de um usuário) com latência de milissegundos, não de minutos.
A equipe de engenharia do Spotify enfrentou esse desafio. Seus exabytes de dados no GCS eram perfeitos para processamento em lote, mas lentos para consultas pontuais online. O gargalo não era a camada de armazenamento—o GCS entrega uma requisição em 30-100ms, e o S3 Express One Zone ainda mais rápido. O problema real eram os query engines: engines SQL distribuídos como Trino ou BigQuery adicionam segundos de overhead para agendamento e planejamento, mesmo para um simples lookup.
Neste mergulho profundo, vamos explorar o Random Access Parquet (RAP), uma técnica desenvolvida pelo Spotify para preencher essa lacuna. O RAP permite consultas pontuais interativas diretamente nos seus arquivos Parquet existentes, sem copiar dados para um key-value store separado. Vamos cobrir o conceito central, o índice externo e um conjunto de otimizações de layout de arquivo que podem reduzir drasticamente a latência.

A Ideia Central: Substituindo Leituras Dependentes por um Único Lookup
O problema fundamental com consultas pontuais em um data lake é a cadeia de leituras dependentes. Para encontrar uma única linha em um arquivo Parquet, o engine precisa:
- Buscar o rodapé do arquivo.
- Parsear o metadata dos row groups.
- Escanear a coluna de chave para localizar as linhas correspondentes.
- Usar índices de coluna e página para encontrar as páginas correspondentes em cada coluna de valor.
Cada passo requer um round-trip ao armazenamento, adicionando latência. A abordagem RAP elimina essa cadeia usando um índice externo que mapeia cada chave diretamente para o arquivo e o número da linha onde seus dados residem. Dada uma chave, o leitor pode emitir leituras de intervalo precisas em paralelo, buscando apenas os bytes necessários.
Estrutura do Índice Externo
O índice é um multimapa, onde uma única chave pode ter entradas em vários arquivos e partições. Cada entrada contém:
- Chave: A chave de lookup (ex: user ID).
- Arquivo: Qual arquivo Parquet (ordinal codificado por dicionário).
- Números de linha: As linhas dentro daquele arquivo.
- Contagem de valores (opcional): Para paginação.
Isso é fundamentalmente diferente dos page indexes ou Bloom filters nativos do Parquet, que são probabilísticos e estreitam uma varredura. O índice externo é definitivo—retorna os arquivos e linhas exatos, eliminando a varredura por completo.
Exemplo de Código: Um Simples Lookup de Índice RAP
Embora a implementação completa seja complexa, aqui está um exemplo simplificado em Python para ilustrar o conceito:
# Exemplo simples de lookup de índice RAP (pseudo-código)
def get_user_data(user_id: str, rap_index: dict) -> list:
"""
Recupera dados do usuário usando um índice RAP.
"""
# 1. Consulta o índice (operação O(1))
if user_id not in rap_index:
return []
# 2. Obtém os arquivos e números de linha
entries = rap_index[user_id]
results = []
for entry in entries:
file_path = entry['file']
row_numbers = entry['row_numbers']
# 3. Lê apenas as linhas específicas do arquivo Parquet
# (usando pyarrow ou similar)
data = read_parquet_rows(file_path, row_numbers)
results.extend(data)
return results
# Exemplo de índice (na realidade, seria distribuído)
rap_index = {
'user_123': [
{'file': 's3://data-lake/2026/07/01/user_events.parquet', 'row_numbers': [42, 43]},
{'file': 's3://data-lake/2026/07/02/user_events.parquet', 'row_numbers': [10]}
]
}
# Consulta
user_data = get_user_data('user_123', rap_index)
print(user_data)
Nota: Este é um exemplo simplificado. Implementações de produção lidam com índices distribuídos, caching e leituras paralelas.

Otimizando o Layout do Arquivo para Consultas Pontuais
O índice externo diz onde ler, mas o layout do arquivo determina quanto você lê. Para minimizar latência e I/O, o RAP aplica várias otimizações que concentram os dados de uma chave e reduzem o número de operações de leitura.
Resumo das Principais Otimizações
| Otimização | Benefício para Consulta Pontual | Tradeoff para Análise |
|---|---|---|
| Ordenação por chave | Menos arquivos e páginas por chave | Nenhum |
| Co-agrupamento | Uma linha por chave; naturalmente concentrado | Nenhum |
| Particionamento mais grosseiro | Menos arquivos por chave ao longo do tempo | Pruning de partição mais grosseiro |
| Uma página por chave | A página inteira é o resultado | Crescimento modesto do PageIndex |
| Reset de frames ZSTD | Acesso O(1) sem proliferação de páginas | Apenas codificação PLAIN; aumento modesto do tamanho do arquivo |
| Blobs / Variants | Leitura de uma única coluna por chave | Sem pruning por campo |
| Intercalação de colunas | Uma única leitura contígua para todas as colunas | Aumento de I/O para varreduras de coluna única |
| Alinhamento de armazenamento | Sem amplificação de leitura nas fronteiras | Aumento modesto do tamanho do arquivo |
| Índice de cobertura | Nenhuma leitura de armazenamento | Aumento do tamanho do índice |
A Maior Vitória: Reduzindo Operações de Leitura
Em um arquivo Parquet padrão, buscar os valores de uma chave em N colunas requer N leituras paralelas. A otimização mais eficaz é reduzir o número de leituras para uma. Isso pode ser alcançado por:
- Armazenar dados como uma única coluna blob ou Variant (ex: JSON, Protobuf). Isso é natural para aplicações que consomem os dados como um documento.
- Intercalação de colunas: Colocar fisicamente dados de diferentes colunas adjacentes para cada chave. Isso permite uma única leitura de intervalo contígua para buscar todas as colunas de uma vez, permanecendo um Parquet válido para leitores padrão.
![]()
O Futuro dos Data Lakes: Uma Única Camada de Serviço
O RAP tem implicações significativas para a arquitetura de dados. Ele permite que o data lake sirva tanto para workloads analíticas quanto interativas, eliminando a necessidade de manter cópias separadas em sistemas de serviço especializados. Isso muda a economia de quais dados podem ser servidos online—dados históricos, entidades de cauda longa e features de baixo tráfego tornam-se viáveis para acesso interativo.
No entanto, o RAP não é uma bala de prata. Requer construir e manter um índice externo, e as otimizações de layout de arquivo podem não ser adequadas para todos os workloads. Por exemplo, a intercalação de colunas pode prejudicar o desempenho para varreduras de coluna única em análises em lote.
Limitações e Considerações:
- Manutenção do índice: O índice deve ser construído e atualizado conforme novos dados chegam, adicionando complexidade ao pipeline.
- Tradeoffs de layout de arquivo: Otimizações como intercalação de colunas podem impactar negativamente o desempenho de consultas em lote.
- Tipos de dados: Reset de frames ZSTD requer codificação PLAIN, que pode ser menos compacta para certos tipos de dados.
Próximos Passos para Aprender:
- Experimente com os internals do Parquet: Use ferramentas como
pyarrow.parquetpara entender row groups, páginas e índices de coluna. - Explore índices secundários: Aprenda a implementar hash tables e índices ordenados para lookups multidimensionais.
- Considere curvas de preenchimento de espaço: Técnicas como Z-ordering podem complementar índices secundários para melhor localidade de dados.
Junto com tendências relacionadas, como a evolução da IA no dispositivo, a capacidade de acessar e raciocinar rapidamente sobre grandes conjuntos de dados torna-se ainda mais crítica. Para mais sobre isso, veja nossa análise sobre function calling on-device no Google AI Edge.
Leituras Recomendadas: