Planteamiento y alcance
Eres responsable de una tarea de analítica local. DuckDB lee archivos Parquet y ejecuta operaciones JOIN de múltiples tablas, GROUP BY y funciones de ventana. A medida que los datos crecen, la tarea reporta Out of Memory o genera demasiados archivos temporales y agota el tiempo de espera (timeout). Explica tu orden de diagnóstico, cambios de parámetros, reescrituras de SQL y plan de verificación.
Este escenario se adapta a entrevistas de ingeniería de datos, ingeniería analítica y OLAP embebido. La respuesta debe basarse en evidencias; "agregar memoria" o "comprar una máquina más grande" no es un diagnóstico.
Qué evalúa el entrevistador
- Si distingues la ejecución por transmisión (streaming) de los operadores que retienen un estado grande.
- Si utilizas planes, perfiles de tiempo de ejecución y señales de memoria en lugar de adivinar.
- Si comprendes los subprocesos (threads), los límites de memoria, los directorios de desbordamiento (spill) y la preservación del orden de inserción.
- Si verificas la capacidad del disco temporal, los permisos, los tipos, los índices y la explosión de resultados en los JOIN.
- Si la corrección, la reproducibilidad y el rendimiento frente a regresiones cierran el ciclo de optimización.
Preguntas de clarificación antes de responder
- ¿El fallo ocurre durante el escaneo, JOIN, agregación, ordenamiento o una ventana? ¿Es un error de DuckDB o una terminación forzada por el sistema operativo?
- ¿Cuáles son la versión de DuckDB, el recuento de subprocesos,
memory_limit, la ruta del directorio temporal y el espacio disponible en disco? - ¿La entrada es Parquet/CSV, cuáles son los tipos de columnas y el diseño de particiones, y se pueden aplicar predicados hacia abajo (predicate pushdown)?
- ¿Contiene la consulta GROUP BY de alta cardinalidad, DISTINCT exacto, JOINs amplios, ORDER BY, ventanas,
list/string_aggo PIVOT? - ¿Se puede procesar el resultado en lotes, pre-agregar, aproximar o devolver sin preservar el orden de entrada?
Estructura de respuesta de 30 segundos
Primero identifico el operador y el recurso que se han agotado, luego establezco una línea base con EXPLAIN ANALYZE, instantáneas de memoria y métricas del directorio temporal. Si una agregación de alta cardinalidad, JOIN, ordenamiento o ventana crea un estado bloqueante, reduzco las filas y columnas escaneadas y corrijo las condiciones de filtro o unión antes de reducir la concurrencia, configurar un límite de memoria seguro y validar un directorio de desbordamiento. Finalmente, comparo los recuentos de filas, la unicidad de claves, las agregaciones y la latencia en entradas fijas y completas para que la optimización sea más rápida y semánticamente segura.
Análisis detallado paso a paso
1. Separar el fallo de memoria del fallo de disco temporal
Registra el texto del error, el motivo de salida del proceso, el RSS máximo, la versión de DuckDB, el recuento de subprocesos y la huella digital de la consulta. DuckDB reserva parte de la memoria disponible como límite, pero un OOM del sistema operativo, el límite del contenedor, un directorio temporal sin permisos de escritura o un disco lleno pueden parecer similares. Verifica los límites de cgroup/contenedor, la capacidad y los permisos antes de modificar el SQL.
2. Encontrar el operador bloqueante en el plan
Usa EXPLAIN para inspeccionar el orden de unión y el empuje de predicados, luego EXPLAIN ANALYZE para ver filas reales, tiempos y el estado en tiempo de ejecución. Los escaneos generalmente procesan fragmentos (chunks), mientras que GROUP BY, JOIN, ORDER BY, las ventanas y DISTINCT exacto retienen tablas hash, búferes de ordenamiento o marcos. Si una clave de unión incorrecta multiplica las filas, corrige su semántica antes de debatir sobre la memoria.
3. Reducir el conjunto de trabajo antes de ajustar recursos
Lee únicamente las columnas necesarias, añade filtros de partición y tiempo de forma temprana, y evita materializar una relación ancha completa dentro de una subconsulta. Pre-agrega hechos costosos reutilizables por partición y divide un JOIN evidente de muchos a muchos en pasos con comprobaciones de unicidad. No reemplaces una estadística exacta de alta cardinalidad con una aproximación sin un margen de error explícito.
4. Configurar y verificar subprocesos, memoria y desbordamiento (spilling)
Más subprocesos pueden hacer que varios operadores retengan estado simultáneamente; por lo tanto, reduce threads en un host restringido. Mantén memory_limit por debajo del presupuesto del contenedor para dejar margen al sistema; simplemente aumentarlo puede convertir un error de DuckDB en una terminación por el SO. Para el desbordamiento, utiliza un disco local rápido con permisos de escritura y capacidad conocida. Un experimento controlado puede usar:
SET threads = 4;
SET memory_limit = '4GB';
SET temp_directory = '/var/tmp/duckdb_swap';
SET preserve_insertion_order = false;
EXPLAIN ANALYZE
SELECT customer_id, date_trunc('day', event_time) AS day, sum(amount) AS total
FROM read_parquet('events/*.parquet')
WHERE event_time >= DATE '2026-01-01'
GROUP BY customer_id, day;Deshabilita preserve_insertion_order solo cuando el resultado del negocio no dependa del orden de entrada. Los índices y algunos estados intermedios no están necesariamente gobernados por el administrador de búferes, por lo que memory_limit no es una protección estricta universal.
5. Identificar el comportamiento de desbordamiento y los límites del operador
El desbordamiento admite muchas cargas de trabajo grandes de GROUP BY, JOIN, ordenamiento y ventanas, pero agrega E/S. Los operadores bloqueantes encadenados, las agregaciones de listas gigantescas, string_agg, algunas agregaciones holísticas y PIVOT aún pueden requerir un estado indivisible grande. Si el directorio temporal crece de forma inesperada, inspecciona temp_directory, max_temp_directory_size, el rendimiento del disco y la limpieza. Cuando el desbordamiento no pueda ayudar, regresa al procesamiento por lotes o reescribe la estructura de SQL.
6. Cerrar con regresiones de resultados y rendimiento
Utiliza una instantánea de entrada fija para comparar filas totales, conjuntos de claves primarias, distribución de NULL, recuentos de grupos, sumas de verificación y detalles de muestra antes y después. Registra la memoria máxima, los bytes temporales, los bytes escaneados, el tiempo de ejecución y la tasa de fallos. Prueba fechas límite, particiones vacías, claves duplicadas y cardinalidades extremas por separado.
Respuesta de ejemplo de alta calidad
Clasifico el incidente como estado del operador, capacidad de configuración o entorno externo. Primero preservo la versión, la consulta, la instantánea de entrada, la memoria del contenedor y las evidencias del disco temporal; luego utilizo EXPLAIN ANALYZE para ubicar el pico. Para un GROUP BY de alta cardinalidad, un JOIN incorrecto de muchos a muchos, un ordenamiento o una ventana, verifico la cardinalidad y el empuje de predicados, reduzco columnas y filas, y pre-agrego cuando sea apropiado; no oculto la explosión de una unión aumentando memory_limit.
A continuación, reduzco la concurrencia de subprocesos a un nivel seguro medido, dejo margen para el sistema por debajo de memory_limit y ubico temp_directory en un disco con capacidad y permisos explícitos. Deshabilito preserve_insertion_order solo cuando el orden no sea contractual. Mido la memoria máxima, los bytes desbordados y el tiempo de ejecución, y verifico que el uso temporal permanezca dentro de la cuota. Para estados de lista, cadenas muy grandes o PIVOT que no se pueden dividir eficazmente, utilizo resultados por etapas o reconsidero la estructura de la consulta.
Por último, comparo los recuentos de filas, la unicidad de claves, las sumas de verificación de agregaciones, las particiones límite y el comportamiento de NULL en datos fijos y completos antes del lanzamiento. Esto demuestra tanto que el OOM ha desaparecido como que se preservó la semántica del resultado.
Errores comunes
Aumentar únicamente memory_limit
Sin verificar el límite del contenedor, el margen del sistema y la memoria no administrada por el búfer, el fallo puede pasar de DuckDB al sistema operativo.
Asumir que cada operador puede desbordarse a disco
Confirma el operador específico y la versión. Algunos estados de lista, cadenas, agregaciones holísticas y PIVOT aún necesitan memoria indivisible.
Ignorar la cardinalidad de los JOIN y el empuje de predicados
Claves no únicas o filtros tardíos pueden generar órdenes de magnitud más de datos intermedios; los ajustes de configuración no pueden reparar una estructura de consulta errónea.
Verificar el éxito sin regresión de resultados
Cambiar la preservación del orden, dividir agregaciones o usar funciones aproximadas puede alterar la semántica. Compara entradas fijas y comprobaciones del negocio.
Preguntas de seguimiento y respuestas
Pregunta de seguimiento 1: ¿Por qué puede ayudar tener menos subprocesos?
Los operadores concurrentes pueden retener estados y búferes al mismo tiempo. Menos subprocesos reducen el pico pero generalmente reducen el rendimiento (throughput), por lo que se debe elegir a partir de curvas medidas de memoria y tiempo de finalización.
Pregunta de seguimiento 2: ¿Por qué puede persistir el OOM cuando el disco temporal tiene espacio?
No todos los estados se pueden particionar y desbordar. Las agregaciones indivisibles, el estado de unión sobredimensionado o los permisos y cuotas de directorios aún pueden fallar; combina el plan, los límites y los registros.
Pregunta de seguimiento 3: ¿Cuándo se puede deshabilitar la preservación del orden de inserción?
Solo cuando el resultado y los consumidores posteriores no traten el orden de entrada como un contrato. Realiza pruebas de regresión en claves duplicadas, ordenamiento y comportamiento de LIMIT después.
Pregunta de seguimiento 4: ¿Cómo demuestras que la optimización preservó los resultados?
Ejecuta la misma instantánea de entrada y compara recuentos de filas, conjuntos de claves primarias, recuentos de grupos, sumas de verificación numéricas, distribuciones de NULL y particiones límite. Para agregaciones aproximadas, declara el margen de error y obtén la aprobación del negocio.