Del Batch al Interactivo: La Revolución del Data Lake
El data lake ha sido tradicionalmente el hogar del análisis por lotes—procesando petabytes de datos con consultas SQL masivas para informes y pipelines de ML. Pero con el auge de los agentes de IA y la personalización en tiempo real, surge la necesidad de acceder a registros individuales (por ejemplo, el historial de escucha de un usuario) con una latencia de milisegundos, no de minutos.
El equipo de ingeniería de Spotify enfrentó este desafío. Sus exabytes de datos en GCS eran perfectos para el procesamiento por lotes, pero demasiado lentos para consultas puntuales en línea. El cuello de botella no era la capa de almacenamiento—GCS entrega una solicitud en 30-100ms, y S3 Express One Zone incluso más rápido. El problema real eran los motores de consulta: los motores SQL distribuidos como Trino o BigQuery añaden segundos de sobrecarga para la programación y planificación, incluso para una simple búsqueda.
En esta inmersión profunda, exploraremos el Random Access Parquet (RAP), una técnica desarrollada por Spotify para cerrar esta brecha. RAP permite consultas puntuales interactivas directamente sobre tus archivos Parquet existentes, sin copiar datos a un key-value store separado. Cubriremos el concepto central, el índice externo y un conjunto de optimizaciones de layout de archivo que pueden reducir drásticamente la latencia.

La Idea Central: Reemplazar Lecturas Dependientes con un Solo Lookup
El problema fundamental con las consultas puntuales en un data lake es la cadena de lecturas dependientes. Para encontrar una sola fila en un archivo Parquet, el motor debe:
- Obtener el pie de página del archivo.
- Analizar los metadatos de los row groups.
- Escanear la columna de clave para localizar las filas correspondientes.
- Usar los índices de columna y página para encontrar las páginas correspondientes en cada columna de valor.
Cada paso requiere un viaje de ida y vuelta al almacenamiento, añadiendo latencia. El enfoque RAP elimina esta cadena usando un índice externo que mapea cada clave directamente al archivo y número de fila donde residen sus datos. Dada una clave, el lector puede emitir lecturas de rango precisas en paralelo, obteniendo solo los bytes necesarios.
Estructura del Índice Externo
El índice es un multimapa, donde una sola clave puede tener entradas en múltiples archivos y particiones. Cada entrada contiene:
- Clave: La clave de búsqueda (ej: ID de usuario).
- Archivo: Qué archivo Parquet (ordinal codificado por diccionario).
- Números de fila: Las filas dentro de ese archivo.
- Conteo de valores (opcional): Para paginación.
Esto es fundamentalmente diferente de los índices de página o filtros Bloom nativos de Parquet, que son probabilísticos y reducen el alcance de un escaneo. El índice externo es definitivo—devuelve los archivos y filas exactos, eliminando el escaneo por completo.
Ejemplo de Código: Un Simple Lookup de Índice RAP
Aunque la implementación completa es compleja, aquí hay un ejemplo simplificado en Python para ilustrar el concepto:
# Ejemplo simple de lookup de índice RAP (pseudo-código)
def get_user_data(user_id: str, rap_index: dict) -> list:
"""
Recupera datos del usuario usando un índice RAP.
"""
# 1. Consulta el índice (operación O(1))
if user_id not in rap_index:
return []
# 2. Obtiene los archivos y números de fila
entries = rap_index[user_id]
results = []
for entry in entries:
file_path = entry['file']
row_numbers = entry['row_numbers']
# 3. Lee solo las filas específicas del archivo Parquet
# (usando pyarrow o similar)
data = read_parquet_rows(file_path, row_numbers)
results.extend(data)
return results
# Ejemplo de índice (en realidad, sería distribuido)
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 es un ejemplo simplificado. Las implementaciones de producción manejan índices distribuidos, caché y lecturas paralelas.

Optimizando el Layout del Archivo para Consultas Puntuales
El índice externo dice dónde leer, pero el layout del archivo determina cuánto lees. Para minimizar la latencia y el I/O, RAP aplica varias optimizaciones que concentran los datos de una clave y reducen el número de operaciones de lectura.
Resumen de las Principales Optimizaciones
| Optimización | Beneficio para Consulta Puntual | Tradeoff para Análisis |
|---|---|---|
| Ordenación por clave | Menos archivos y páginas por clave | Ninguno |
| Co-agrupación | Una fila por clave; naturalmente concentrado | Ninguno |
| Particionamiento más grueso | Menos archivos por clave a lo largo del tiempo | Poda de partición más gruesa |
| Una página por clave | La página completa es el resultado | Crecimiento modesto del PageIndex |
| Reset de frames ZSTD | Acceso O(1) sin proliferación de páginas | Solo codificación PLAIN; aumento modesto del tamaño del archivo |
| Blobs / Variants | Lectura de una sola columna por clave | Sin poda por campo |
| Intercalación de columnas | Una sola lectura contigua para todas las columnas | Aumento de I/O para escaneos de columna única |
| Alineación de almacenamiento | Sin amplificación de lectura en los límites | Aumento modesto del tamaño del archivo |
| Índice de cobertura | Ninguna lectura de almacenamiento | Aumento del tamaño del índice |
La Mayor Victoria: Reducir las Operaciones de Lectura
En un archivo Parquet estándar, obtener los valores de una clave en N columnas requiere N lecturas paralelas. La optimización más efectiva es reducir el número de lecturas a una. Esto se puede lograr:
- Almacenando datos como una sola columna blob o Variant (ej: JSON, Protobuf). Esto es natural para aplicaciones que consumen los datos como un documento.
- Intercalación de columnas: Colocar físicamente datos de diferentes columnas adyacentes para cada clave. Esto permite una sola lectura de rango contigua para obtener todas las columnas a la vez, permaneciendo como un Parquet válido para los lectores estándar.

El Futuro de los Data Lakes: Una Única Capa de Servicio
RAP tiene implicaciones significativas para la arquitectura de datos. Permite que el data lake sirva tanto para cargas de trabajo analíticas como interactivas, eliminando la necesidad de mantener copias separadas en sistemas de servicio especializados. Esto cambia la economía de qué datos pueden servirse en línea—los datos históricos, las entidades de cola larga y las funciones de bajo tráfico se vuelven viables para acceso interactivo.
Sin embargo, RAP no es una bala de plata. Requiere construir y mantener un índice externo, y las optimizaciones de layout de archivo pueden no ser adecuadas para todas las cargas de trabajo. Por ejemplo, la intercalación de columnas puede perjudicar el rendimiento para escaneos de columna única en análisis por lotes.
Limitaciones y Consideraciones:
- Mantenimiento del índice: El índice debe construirse y actualizarse a medida que llegan nuevos datos, añadiendo complejidad al pipeline.
- Tradeoffs de layout de archivo: Optimizaciones como la intercalación de columnas pueden impactar negativamente el rendimiento de consultas por lotes.
- Tipos de datos: El reset de frames ZSTD requiere codificación PLAIN, que puede ser menos compacta para ciertos tipos de datos.
Próximos Pasos para Aprender:
- Experimenta con los internals de Parquet: Usa herramientas como
pyarrow.parquetpara entender row groups, páginas e índices de columna. - Explora índices secundarios: Aprende a implementar tablas hash e índices ordenados para búsquedas multidimensionales.
- Considera curvas de llenado de espacio: Técnicas como Z-ordering pueden complementar los índices secundarios para una mejor localidad de datos.
Junto con tendencias relacionadas, como la evolución de la IA en el dispositivo, la capacidad de acceder y razonar rápidamente sobre grandes conjuntos de datos se vuelve aún más crítica. Para más sobre esto, mira nuestro análisis sobre function calling on-device en Google AI Edge.
Lecturas Recomendadas: