Tema representativo de entrevista

Entrevista de Ingeniería de Datos: ¿Cómo diagnosticar y solucionar el problema de archivos pequeños en un Lakehouse?

DatosDifícil
Equipo editorial de Offer.ccPublicado Actualizado

Pregunta

Una tabla de eventos de Iceberg de 20 TiB recibe 200 GiB por día pero crea aproximadamente 80,000 archivos Parquet, y el tiempo de planificación de consultas sigue aumentando. ¿Cómo encontraría la causa, detendría los nuevos archivos pequeños y compactaría el trabajo acumulado de forma segura?

Planteamiento y contexto aplicable

Una tabla de eventos de Apache Iceberg en almacenamiento de objetos tiene 20 TiB de datos activos. Un trabajo de streaming realiza commits de micro-lotes de un minuto. Añade aproximadamente 200 GiB por día, pero crea alrededor de 80,000 archivos de datos Parquet cuyo tamaño mediano es de solo 3 MiB. El p95 de las consultas ha aumentado recientemente de 20 segundos a 95 segundos, mientras que la fase de planificación aumentó de 4 segundos a 32 segundos. El negocio debe mantener una ingesta casi en tiempo real, conservar siete días de time travel y nunca eliminar objetos de la tabla directamente del almacenamiento.

Explique cómo demostraría que los archivos pequeños dominan la regresión, encontraría las causas de escritura y particionamiento, detendría el crecimiento y compactaría los archivos existentes de manera segura. Incluya control de concurrencia, presupuestación de recursos, rollback y criterios de aceptación.

Todas las capacidades, recuentos de archivos, latencias y valores de rendimiento (throughput) son suposiciones de entrevista, no puntos de referencia universales. Esta pregunta se adapta a roles de ingeniería de datos, plataforma de analítica, infraestructura de lakehouse y SRE de plataforma de datos. Su competencia principal es el diseño de datos (data layout) y el mantenimiento de tablas, por lo que la categoría es data; no es simplemente una pregunta de configuración de Spark o de operaciones de almacenamiento de objetos.

Qué evalúa el entrevistador

Primero, ¿puede el candidato construir una cadena de evidencia? Un recuento alto de archivos por sí solo no establece causalidad. El recuento de archivos activos, la distribución de tamaño, el sesgo de partición (partition skew), el tiempo de lectura de manifiestos, el inicio de tareas y la sobrecarga de apertura de archivos deben relacionarse por separado con la planificación y el escaneo de consultas.

Segundo, ¿puede el candidato distinguir una solución de trabajo acumulado (backlog) de la prevención? La compactación maneja los archivos existentes. Si la frecuencia de micro-lotes, el número de writers, la distribución de datos y un particionamiento demasiado granular permanecen sin cambios, la tabla se fragmentará de nuevo.

Tercero, ¿respeta el candidato los límites de transacción del formato de tabla? La compactación de Iceberg reescribe los archivos de datos y realiza el commit de un nuevo snapshot. Los snapshots más antiguos aún pueden hacer referencia a archivos viejos, por lo que eliminar objetos a espaldas de los metadatos no es seguro.

Cuarto, ¿puede el candidato tomar decisiones de balance (trade-offs) acotadas? El bin packing cambia principalmente los tamaños de archivo. La ordenación (sorting) o el Z-ordering también cambian el agrupamiento (clustering) y pueden mejorar el pruning, pero cuestan más shuffle, ordenación y almacenamiento temporal.

Quinto, ¿puede el candidato cuantificar un plan operativo? Una respuesta sólida estima los bytes reescritos por día, el recuento de archivos objetivo, la ventana de trabajo y el riesgo de conflictos, y luego utiliza métricas tanto de corrección como de rendimiento para decidir si expandir el despliegue.

Preguntas de aclaración antes de responder

  • ¿Qué objetivo define a un archivo como "pequeño"? Lea write.target-file-size-bytes, luego elija umbrales utilizando la selectividad de consultas, el volumen diario por partición y pruebas en el motor. Un tamaño fijo no es correcto para todas las tablas.
  • ¿Están los 80,000 archivos activos en el snapshot actual o se cuentan en snapshots históricos? Comience con files para la ruta de consulta; use all_files y referencias de snapshots para retención y costo de almacenamiento.
  • ¿La latencia está en la planificación o en el escaneo? Una proporción creciente de planificación apunta hacia manifiestos y tareas de archivos. Un rendimiento de escaneo decreciente también requiere verificar el sesgo, los archivos de eliminación (delete files), la compresión, las estadísticas de columnas y los recursos downstream.
  • ¿Cuáles son la especificación de partición y la distribución de escritura? Particiones de alta cardinalidad o de tiempo demasiado granulares pueden nunca acumular un archivo del tamaño objetivo. Writers paralelos excesivos pueden realizar commits de un archivo parcialmente lleno cada uno.
  • ¿Utiliza la tabla copy-on-write o merge-on-read? Merge-on-read puede acumular archivos de eliminación posicionales o de igualdad, por lo que compactar solo los archivos de datos puede no ser suficiente.
  • ¿Qué particiones todavía reciben datos tardíos? Prefiera particiones cerradas y frías. Las particiones activas (hot) requieren grupos de archivos más pequeños, concurrencia controlada y reintentos de conflictos.
  • ¿Qué significa una retención de siete días? Confirme por separado la consultabilidad de snapshots, la retención de branches o tags y el ciclo de vida del almacenamiento de objetos. Una eliminación de directorio no puede sustituir a los tres.

Marco de respuesta de 30 segundos

"Perfilaría los archivos activos, los percentiles de tamaño y las particiones del snapshot actual de Iceberg, luego separaría la planificación del escaneo. Con 200 GiB por día, 80,000 archivos frente a aproximadamente 400 archivos ideales de 512 MiB convierte a los micro-lotes, los writers y la granularidad de partición en los primeros sospechosos.

Detendría la nueva fragmentación ampliando los lotes, distribuyendo por clave de partición y controlando el número de writers. Luego aplicaría bin packing a una partición fría con concurrencia acotada, ordenando solo cuando las pruebas de pruning lo justifiquen. La compactación realiza el commit de un nuevo snapshot; los archivos antiguos siguen la retención de siete días y nunca se eliminan directamente. La conciliación de datos, los percentiles de archivos, el p95 de planificación y consulta, el lag y los conflictos determinan si el despliegue se expande".

Análisis detallado paso a paso

Paso 1: Perfilar archivos del snapshot actual

Consulte los metadatos de Iceberg en lugar de listar recursivamente el directorio del almacenamiento de objetos. El directorio puede contener archivos referenciados solo por snapshots históricos u objetos huérfanos, por lo que no representa lo que planifica una consulta actual. El siguiente SQL ilustra un catálogo de Spark e Iceberg; adapte el nombre del catálogo y la función de percentil al motor real:

sql
SELECT
  partition,
  COUNT(*) AS active_files,
  SUM(file_size_in_bytes) AS active_bytes,
  percentile_approx(file_size_in_bytes, array(0.5, 0.9, 0.99)) AS size_percentiles
FROM lakehouse.analytics.events.files
GROUP BY partition
ORDER BY active_files DESC;

Registre el ID del snapshot actual, los recuentos de data files y delete files, el recuento de manifiestos, los percentiles de tamaño de archivo por partición y el porcentaje de archivos por debajo de un umbral candidato. Una media oculta la cola larga: una partición puede tener unos pocos archivos grandes y decenas de miles de archivos de 1 a 3 MiB. Como mínimo, inspeccione p50, p90, p99 y un histograma.

Desglose el p95 de la consulta en resolución de catálogo y manifiesto, planificación de archivos, programación de tareas, tiempo hasta el primer byte y escaneo. El caso causal se vuelve más fuerte cuando el recuento de archivos y el tiempo de planificación aumentan juntos y una partición de prueba con el mismo total de bytes se planifica sustancialmente más rápido después de la compactación. Si la planificación es estable pero el escaneo se ralentiza, investigue en su lugar la selectividad, las estadísticas de columnas, los archivos de eliminación, el sesgo y los recursos de cómputo.

Paso 2: Encontrar la causa de regeneración en la ruta de escritura

El escenario produce 1,440 lotes de un minuto por día. Ochenta mil archivos representan unos 56 archivos por lote en promedio. Cuando cada writer o combinación de writer-partición recibe muy pocos datos, establecer un objetivo de 512 MiB no puede convertir una salida de 3 MiB en un archivo completo. write.target-file-size-bytes es un objetivo, no una garantía de que cada archivo lo alcance.

La causa raíz suele ser una combinación: los micro-lotes son demasiado frecuentes; el paralelismo upstream es excesivo para cada lote; las filas no están agrupadas por la clave de partición de la tabla antes de escribir; dimensiones horarias, de tenant o de usuario sobreparticionan la tabla; claves activas (hot keys) causan sesgo; los reintentos añaden commits; o las actualizaciones merge-on-read crean deuda de archivos de eliminación.

Interprete "pequeño" por partición. Una partición válida de bajo volumen que recibe solo 40 MiB por día nunca podrá llenar un archivo de 512 MiB. Acepte un objetivo más pequeño, aumente la granularidad de la especificación de partición (coarsen) o use buckets o particionamiento oculto en lugar de aumentar la frecuencia de compactación para siempre.

Paso 3: Detener la producción de la misma fragmentación

Realice el cambio más pequeño en el lado de la escritura mediante un canary. Dentro del SLA de frescura, combine los commits de un minuto en un lote de trigger más grande. Distribuya las filas por hash o por rango según la clave de partición de Iceberg. Elija el recuento de writers a partir de los bytes por lote en lugar del paralelismo máximo del clúster. Evite particionar directamente sobre columnas de alta cardinalidad.

Pruebe el tamaño de archivo objetivo frente a la tasa de compresión real, el ancho de fila, la selectividad de consultas y el volumen diario por partición. El escenario utiliza 512 MiB, o 536,870,912 bytes, como candidato porque el valor predeterminado de Iceberg proporciona un punto de partida razonable. No descarta 128 MiB, 256 MiB o un valor mayor. Si una partición es mucho más pequeña que el objetivo, evolucione la especificación de partición. Si existen suficientes datos pero cada writer recibe pocas filas, corrija la distribución y el paralelismo.

Ejecute una comparación A/B en dos particiones con cargas similares. Compare el recuento de archivos, la distribución de tamaños, la latencia de commit, el lag de procesamiento de streaming y la recuperación ante fallas con un volumen de datos igual. La compactación del trabajo acumulado se vuelve sostenible solo después de que la tasa de generación de nuevos archivos disminuya materialmente.

Paso 4: Acotar la compactación por beneficio y riesgo de conflicto

Para la primera pasada, elija una partición fría cuyas ventanas de datos tardíos y correcciones de negocio se hayan cerrado, y al menos excluya la hora que se está escribiendo actualmente. Que una partición aún pueda cambiar y que los snapshots se retengan durante siete días son líneas de tiempo separadas. Comience con bin packing porque el objetivo inmediato es reducir la sobrecarga de metadatos y apertura de archivos. Pase a ordenación o Z-ordering solo cuando las columnas de filtrado comunes se superpongan en gran medida entre archivos y un benchmark justifique el shuffle adicional.

sql
CALL lakehouse.system.rewrite_data_files(
  table => 'analytics.events',
  strategy => 'binpack',
  options => map(
    'target-file-size-bytes', '536870912',
    'min-input-files', '5',
    'max-concurrent-file-group-rewrites', '3',
    'partial-progress.enabled', 'true'
  ),
  where => 'event_date = DATE ''2026-07-10'''
);

El predicado where selecciona archivos que pueden contener filas coincidentes. Alinéelo con los límites de las particiones e inspeccione los bytes candidatos antes de la ejecución. Los grupos de archivos delimitan cada unidad de trabajo. La concurrencia controlada evita que el almacenamiento de objetos, el shuffle y el clúster de consultas se saturen juntos. El progreso parcial realiza commits de grupos por separado, lo que reduce el costo de reintentar un conflicto, pero crea múltiples snapshots y requiere monitoreo y rollback a nivel de grupo.

Si no se pueden evitar las particiones activas, reduzca el rango de tiempo y los grupos de archivos, haga que la programación sea idempotente y distinga los conflictos de archivos de datos de los conflictos reintentables de commit de metadatos. Nunca ejecute dos trabajos de compactación con rangos de tabla superpuestos.

Paso 5: Estimar el recuento de archivos objetivo, I/O y ventana

Dividir 200 GiB entre 512 MiB da un recuento ideal de aproximadamente 400 archivos objetivo. Los límites de partición, la compresión y los restos residuales hacen que el recuento real sea algo mayor. La estimación detecta errores de orden de magnitud; no es una promesa de exactamente 400 salidas.

Compactar una partición diaria completa lee unos 200 GiB y escribe unos 200 GiB, es decir, aproximadamente 400 GiB de I/O de datos, más shuffle, almacenamiento temporal, metadatos y reintentos. Si un benchmark mide un rendimiento sostenido de extremo a extremo de 100 MiB/s por bytes de entrada, la duración ideal es:

text
200 GiB * 1024 MiB/GiB / 100 MiB/s = 2,048 s ≈ 34.1 min

Añada margen para el sesgo, consultas concurrentes y reintentos. Una ventana de 60 a 90 minutos es un presupuesto razonable para el escenario, junto con límites en bytes candidatos, grupos de archivos concurrentes, tasa de solicitudes al almacenamiento de objetos y disco temporal. Si la compactación solo puede procesar 150 GiB por día mientras llegan 200 GiB, el trabajo acumulado aumentará. Aumente el rendimiento sostenible o reduzca primero la creación de nuevos archivos.

Paso 6: Separar la retención de snapshots de la limpieza física

Después de la compactación, el nuevo snapshot hace referencia a archivos grandes. Una consulta que ya esté leyendo un snapshot más antiguo aún puede finalizar, y el time travel de siete días todavía necesita los archivos antiguos. Eliminar directamente los objetos Parquet originales rompería ambos comportamientos.

Valide el nuevo snapshot y observe un ciclo de negocio completo antes de ejecutar expire_snapshots. Conserve la ventana de siete días, las branches o tags requeridas y un recuento mínimo de snapshots. La expiración de snapshots elimina solo los archivos que ya no son requeridos por ningún snapshot retenido. Los archivos huérfanos son una clase diferente: ningún metadato de tabla hace referencia a ellos. Ejecute remove_orphan_files por separado, comience con dry_run, elija un older_than conservador y verifique los esquemas de ruta, autoridades y la escritura en curso más larga antes de eliminar.

La expiración de snapshots no es compactación, y la limpieza de huérfanos no es un sustituto de la expiración de referencias de snapshots antiguos. Asigne a los tres horarios, permisos y registros de auditoría independientes.

Paso 7: Canary, validación y definición de condiciones de parada

Fije el snapshot previo a la compactación y la partición candidata. Registre el recuento de filas, el recuento de claves de negocio distintas, agregados críticos de montos o eventos, el tiempo mínimo y máximo de eventos y los recuentos de nulos. Recalcule el mismo rango lógico después. El recuento total de filas por sí solo puede ocultar una fila perdida compensada por un duplicado, por lo que las tablas críticas deben agregar sumas de comprobación (checksums) por buckets o muestras de claves de negocio.

Para el rendimiento, compare el recuento de archivos activos, el tamaño p50/p90/p99, el recuento de manifiestos, el p50/p95 de planificación, el p95 de consulta de extremo a extremo, los bytes escaneados, los bytes reescritos y el costo de recursos. Las métricas operativas incluyen la tasa de generación de archivos pequeños, el lag de compactación, los grupos de archivos fallidos, los conflictos de commit, el recuento de snapshots y los bytes recuperables.

Detenga la expansión si la corrección difiere, el tiempo de planificación no mejora, el costo interfiere con el SLA de ingesta o el rendimiento de compactación permanece por debajo de la nueva tasa de fragmentación. Un snapshot de tabla puede revertir el puntero de metadatos, pero no puede revertir por sí mismo una expiración de snapshots y eliminación física ya completadas.

Respuesta de muestra de alta calidad

"No comenzaría ejecutando una compactación. Primero demostraría el cuello de botella. Un directorio de almacenamiento de objetos mezcla archivos históricos y huérfanos, por lo que usaría la tabla de metadatos files del snapshot actual de Iceberg para medir archivos activos, bytes totales y tamaños p50/p90/p99 por partición. Luego separaría la resolución de catálogo y manifiesto, la planificación de tareas, la apertura de archivos y el escaneo. En el escenario, 200 GiB por día crean 80,000 archivos con una mediana de 3 MiB. Un objetivo candidato de 512 MiB implica aproximadamente 400 archivos ideales, por lo que el diseño de escritura es una pista sólida, pero aun así confirmaría que una partición compactada con iguales bytes se planifica más rápido.

A continuación, solucionaría la regeneración. Hay 1,440 lotes de un minuto por día y unos 56 archivos por lote. Inspeccionaría el paralelismo de writers, la cardinalidad de particiones, la distribución previa a la escritura, el sesgo, los reintentos y los delete files de merge-on-read. Dentro del SLA de frescura, ampliaría los lotes de commit, distribuiría por hash o rango según la clave de partición y dimensionaría el recuento de writers a partir de los bytes del lote. El valor de 512 MiB es solo un punto de partida. Una partición de bajo volumen que no pueda llenarlo necesita una partición más gruesa o un objetivo más pequeño.

Para el trabajo acumulado, haría un canary en una partición fría con bin packing mediante rewrite_data_files, un predicado de partición, concurrencia de grupos de archivos acotada y un límite de bytes candidatos. Usaría ordenación o Z-ordering solo si las pruebas de filtros comunes muestran un valor claro, porque la ordenación añade shuffle y almacenamiento temporal. Si se debe procesar una partición activa, usaría grupos de archivos más pequeños, progreso parcial y reintentos de conflicto acotados. Evitaría compactaciones superpuestas.

Con 200 GiB y 512 MiB, la salida ideal es de unos 400 archivos. Una pasada lee unos 200 GiB y escribe 200 GiB. A una tasa medida de extremo a extremo de 100 MiB/s por bytes de entrada, el tiempo de ejecución ideal es de 34.1 minutos; reservaría de 60 a 90 minutos y demostraría que la capacidad diaria supera la entrada diaria.

La compactación realiza el commit atómico de un nuevo snapshot. Las consultas existentes y el time travel de siete días siguen haciendo referencia a archivos antiguos, por lo que nunca eliminaría objetos directamente. Después de validar los recuentos de filas, agregados de negocio, sumas de comprobación en buckets y el p95 de consultas en el nuevo snapshot, expiraría los snapshots bajo la política de siete días. La limpieza de huérfanos sigue siendo un trabajo separado, con dry-run primero y un retraso conservador. El panel de despliegue rastrearía la corrección, la tasa de nuevos archivos pequeños, los percentiles de archivos, el p95 de planificación, el lag de compactación, los conflictos y el beneficio por GiB. Cualquier regresión central detiene la expansión".

Errores comunes

  • Asumir que un recuento alto de archivos demuestra la causa → Los archivos históricos no son archivos de consultas actuales → Perfile el snapshot actual y separe la planificación del escaneo.
  • Ejecutar una compactación y detenerse → Los micro-lotes, writers y particiones finas continúan produciendo fragmentos → Reduzca la tasa de nuevos fragmentos antes de limpiar el trabajo acumulado.
  • Tratar el tamaño objetivo como una garantía estricta → Un writer solo puede generar las filas que recibe → Ajuste el objetivo, los bytes por lote, la distribución y el volumen por partición en conjunto.
  • Reescribir toda la tabla de 20 TiB → El costo y el alcance de los conflictos se vuelven excesivos → Procese particiones frías en lotes acotados por beneficio.
  • Optar por defecto por ordenación o Z-order → Ambos añaden shuffle y almacenamiento temporal → Comience con bin packing y justifique el agrupamiento con pruebas de pruning.
  • Eliminar objetos Parquet antiguos directamente → Los snapshots retenidos y las consultas concurrentes pueden hacerles referencia → Use la expiración de snapshots y una limpieza de huérfanos separada con dry-run.
  • Validar únicamente el recuento total de filas → Una pérdida y un duplicado pueden cancelarse entre sí → Añada agregados de negocio, recuentos de claves, sumas de comprobación en buckets y muestras.
  • Estimar el tiempo de ejecución sin rendimiento sostenible → Un procesamiento diario inferior a la entrada diaria aumenta el trabajo acumulado → Presupueste lecturas, escrituras, shuffle, reintentos y lag de compactación.
  • Usar coalesce(1) como una solución universal → Un solo writer destruye el paralelismo y crea un cuello de botella → Calcule el recuento de writers a partir del volumen de partición y los bytes objetivo.

Preguntas de seguimiento y respuestas

Pregunta de seguimiento 1: ¿Por qué usar un objetivo de 512 MiB en lugar de 128 MiB?

El objetivo predeterminado de Iceberg de 512 MiB, o 536,870,912 bytes, es un punto de partida experimental para la tabla grande de este escenario, no un óptimo universal. Evalúe candidatos de 128, 256 y 512 MiB o mayores frente a la selectividad de consultas, el costo de planificación, el paralelismo de tareas, la compresión y el volumen diario por partición. Las consultas altamente selectivas pueden preferir archivos más pequeños, mientras que los escaneos orientados a throughput y las particiones muy grandes pueden preferir archivos más grandes.

Pregunta de seguimiento 2: ¿Por qué los archivos siguen siendo de solo unos pocos MiB después de aumentar el objetivo?

El objetivo guía el tamaño de salida que un writer intenta alcanzar; no fusiona datos en poder de diferentes tareas. Si un lote de un minuto se divide entre docenas de writers, o un writer toca muchas particiones de bajo volumen, los archivos se cierran cuando la tarea realiza el commit. Cambie el tamaño del lote, la distribución de escritura, el paralelismo y el diseño de particiones en lugar de limitarse a aumentar el objetivo.

Pregunta de seguimiento 3: ¿Cómo elegir entre bin packing, ordenación y Z-ordering?

Elija bin packing cuando el objetivo sea reducir la cantidad de archivos y la sobrecarga de apertura. Pruebe la ordenación cuando las consultas filtren frecuentemente por una columna o una clave jerárquica y las estadísticas de archivo puedan descartar rangos (pruning). Evalúe Z-ordering solo cuando los filtros abarquen comúnmente combinaciones cambiantes de múltiples dimensiones. Incluya el shuffle adicional, el almacenamiento temporal, la amplificación de escritura y el mantenimiento continuo en los benchmarks de estas dos últimas opciones.

Pregunta de seguimiento 4: ¿Qué pasa si la compactación entra en conflicto con las escrituras en streaming?

Excluya primero las particiones activas. Cuando eso sea imposible, use grupos de archivos más pequeños, limite la concurrencia, habilite el progreso parcial y aplique reintentos acotados a los conflictos de commit. El programador debe imponer exclusión mutua para rangos de tabla superpuestos. El progreso parcial evita que un grupo en conflicto obligue a una reejecución completa, pero añade snapshots y estado de éxito parcial, por lo que debe registrar el resultado de cada grupo.

Pregunta de seguimiento 5: ¿Por qué el uso del almacenamiento de objetos no disminuye inmediatamente después de la compactación?

La compactación realiza el commit de un snapshot que hace referencia a archivos nuevos. Los snapshots históricos, branches o tags todavía hacen referencia a archivos viejos para lecturas concurrentes y time travel. Una vez satisfecha la retención de siete días, la expiración de snapshots puede recuperar archivos que ya no son necesarios para los snapshots retenidos. Los archivos a los que no hace referencia ningún metadato de tabla requieren un proceso de limpieza de huérfanos independiente.

Pregunta de seguimiento 6: ¿Cómo demostrar que la ganancia provino de tener menos archivos pequeños y no de la caché o de recursos adicionales?

Utilice la misma configuración de motor y asegúrese de que el estado de la caché sea consistentemente frío o consistentemente caliente. Ejecute consultas repetibles sobre el mismo rango de snapshot lógico y registre el tiempo de planificación, el recuento de tareas, las aperturas de archivos, los bytes escaneados y el tiempo de ejecución antes y después. Mantenga una partición no compactada con carga similar como control y compare los percentiles a lo largo de múltiples ejecuciones. La evidencia causal es más sólida solo cuando el diseño de archivos cambia y la sobrecarga de planificación o apertura disminuye de manera consistente con él.

Fuentes públicas

Preguntas relacionadas