duckdbsparkcomparisondata-lakeperformance

DuckDB vs Spark para Consultas en Data Lakes: Cuándo Gana Cada Uno

Michael San Martim · 2026-04-24

DataSpoc Lens usa DuckDB para consultar archivos Parquet en almacenamiento cloud. Databricks usa Spark. Ambos pueden consultar data lakes. Están construidos para escalas y casos de uso fundamentalmente diferentes. Este artículo te da los datos para elegir el correcto.

La Diferencia Central

DuckDB es una base de datos analítica en proceso. Se ejecuta dentro de tu proceso Python, tu CLI o tu laptop. Cero infraestructura. Un proceso. Una máquina.

Spark es un motor de computación distribuida. Se ejecuta a través de un cluster de máquinas. Requiere infraestructura: un administrador de cluster (YARN, Kubernetes o Databricks), nodos driver, nodos worker, configuración.

DuckDB:
Your laptop → DuckDB (in-process) → Parquet in S3 → Results
Spark:
Your laptop → Spark Driver → Cluster Manager → N Worker Nodes → Parquet in S3 → Shuffle → Results

Comparación de Configuración

DuckDB (vía DataSpoc Lens)

Terminal window
pip install dataspoc-lens
from dataspoc_lens import LensClient
lens = LensClient()
df = lens.query("SELECT region, SUM(amount) FROM curated_sales GROUP BY region")
print(df)

Tiempo hasta la primera consulta: menos de 30 segundos (instalar + ejecutar).

Spark (vía PySpark)

Terminal window
pip install pyspark
# Also need: Java 8+, Hadoop config, S3 credentials config
from pyspark.sql import SparkSession
spark = SparkSession.builder \
.appName("data-lake-query") \
.config("spark.jars.packages", "org.apache.hadoop:hadoop-aws:3.3.4") \
.config("spark.hadoop.fs.s3a.impl", "org.apache.hadoop.fs.s3a.S3AFileSystem") \
.config("spark.hadoop.fs.s3a.aws.credentials.provider",
"com.amazonaws.auth.DefaultAWSCredentialsProviderChain") \
.config("spark.driver.memory", "4g") \
.config("spark.sql.parquet.enableVectorizedReader", "true") \
.getOrCreate()
df = spark.read.parquet("s3a://my-data-lake/curated/sales/")
df.createOrReplaceTempView("curated_sales")
result = spark.sql("SELECT region, SUM(amount) FROM curated_sales GROUP BY region")
result.show()

Tiempo hasta la primera consulta: 5-15 minutos (instalar Java, configurar Hadoop, depurar autenticación S3, esperar inicio de Spark).

En Databricks, la configuración es más rápida (servicio gestionado), pero estás pagando $0.15-0.75 por DBU-hora desde el momento en que inicias un cluster.

Benchmark: Mismas Consultas, Diferentes Escalas

Ejecutamos 5 consultas en 4 tamaños de dataset sobre archivos Parquet en S3. DuckDB se ejecutó en una sola máquina (8 cores, 32GB RAM). Spark se ejecutó en un cluster de 3 nodos (8 cores, 32GB RAM cada uno = 24 cores, 96GB total).

Consulta 1: Agregación Simple

SELECT region, SUM(amount) as total, COUNT(*) as orders
FROM sales
GROUP BY region
ORDER BY total DESC
Tamaño del DatasetDuckDBSparkGanador
1M filas (200MB)0.3s8.2sDuckDB (27x)
10M filas (2GB)1.8s9.5sDuckDB (5x)
100M filas (20GB)14s18sDuckDB (1.3x)
1B filas (200GB)OOM45sSpark

Consulta 2: Join + Agregación

SELECT c.segment, DATE_TRUNC('month', s.sale_date) as month,
SUM(s.amount) as revenue, COUNT(DISTINCT s.customer_id) as customers
FROM sales s
JOIN customers c ON s.customer_id = c.customer_id
GROUP BY c.segment, month
ORDER BY month, revenue DESC
Tamaño del DatasetDuckDBSparkGanador
1M filas0.5s12sDuckDB (24x)
10M filas3.2s14sDuckDB (4x)
100M filas28s22sSpark (1.3x)
1B filasOOM68sSpark

Consulta 3: Función de Ventana

SELECT customer_id, sale_date, amount,
SUM(amount) OVER (PARTITION BY customer_id ORDER BY sale_date) as running_total,
ROW_NUMBER() OVER (PARTITION BY customer_id ORDER BY sale_date DESC) as recency_rank
FROM sales
WHERE sale_date >= '2025-01-01'
Tamaño del DatasetDuckDBSparkGanador
1M filas0.8s15sDuckDB (19x)
10M filas6.5s18sDuckDB (3x)
100M filas52s35sSpark (1.5x)
1B filasOOM120sSpark

Consulta 4: Escaneo Completo de Tabla con Filtro Complejo

SELECT * FROM sales
WHERE amount > 100
AND region IN ('NA', 'EU')
AND sale_date BETWEEN '2025-06-01' AND '2025-12-31'
AND product LIKE '%Pro%'
ORDER BY amount DESC
LIMIT 1000
Tamaño del DatasetDuckDBSparkGanador
1M filas0.2s7sDuckDB (35x)
10M filas0.9s8sDuckDB (9x)
100M filas6s12sDuckDB (2x)
1B filas45s15sSpark (3x)

Consulta 5: Exploración Ad-Hoc

-- "What does this data look like?"
SELECT * FROM sales LIMIT 100
Tamaño del DatasetDuckDBSparkGanador
Cualquier tamaño0.1s5-8sDuckDB (50-80x)

La sobrecarga de Spark es constante: inicialización del cluster, programación de trabajos, distribución de tareas. Para exploración ad-hoc, DuckDB siempre es más rápido porque no hay cluster que coordinar.

Comparación de Costos

DuckDB (vía DataSpoc Lens)

Software: $0 (open-source)
Compute: Your laptop (free) or a VM ($50-150/month)
Storage: S3/GCS/Azure ($0.02/GB/month)
Monthly cost for 500GB data lake: ~$60

Spark en Databricks

Software: Databricks DBU pricing ($0.15-0.75/DBU-hour)
Compute: Cluster instances (e.g., 3x i3.xlarge = $0.94/hour)
Storage: S3/GCS/Azure ($0.02/GB/month)
Cluster uptime: Even "serverless" has startup cost
Monthly cost for 500GB data lake with moderate query load:
Cluster: $200-800/month (depending on uptime)
DBUs: $150-500/month
Storage: $10/month
Total: ~$360-1,310/month

Spark en EMR

EMR fee: $0.015-0.27/hour per instance
EC2 instances: 3x m5.xlarge = $0.576/hour
Storage: $10/month
Monthly cost (8 hours/day usage): ~$300-500/month

Cuándo Gana DuckDB

1. Los Datos Caben en Memoria (< 100GB)

DuckDB procesa datos en memoria en una sola máquina. Para datasets menores a 100GB (lo cual cubre la gran mayoría de data lakes empresariales), es más rápido que Spark porque no hay sobrecarga de cluster.

from dataspoc_lens import LensClient
lens = LensClient()
# 50GB dataset? DuckDB handles it fine on a 64GB machine
df = lens.query("""
SELECT category, DATE_TRUNC('week', event_date) as week,
COUNT(*) as events, COUNT(DISTINCT user_id) as users
FROM curated_events
GROUP BY category, week
ORDER BY week DESC, events DESC
""")

2. Exploración Ad-Hoc y Desarrollo

Cuando estás explorando datos, escribiendo consultas iterativamente y revisando resultados, el tiempo de respuesta sub-segundo de DuckDB cambia cómo trabajas:

# Exploration loop — instant feedback
lens.query("SELECT * FROM curated_sales LIMIT 5") # 0.1s
lens.query("SELECT DISTINCT region FROM curated_sales") # 0.2s
lens.query("SELECT region, COUNT(*) FROM curated_sales GROUP BY 1") # 0.3s
# vs. Spark: 8s, 8s, 9s for the same queries

3. CI/CD y Testing

Ejecutar consultas analíticas en pipelines de CI requiere inicio rápido y cero infraestructura:

# GitHub Actions — test your SQL transformations
- name: Test data transformations
run: |
pip install dataspoc-lens
python -c "
from dataspoc_lens import LensClient
lens = LensClient()
result = lens.query('SELECT COUNT(*) as cnt FROM curated_sales')
assert result['cnt'].iloc[0] > 0, 'Sales table is empty'
"

4. Integración con Agentes de IA

DuckDB inicia instantáneamente, lo cual es crítico para agentes de IA que necesitan ejecutar múltiples consultas en una conversación:

# Agent loop: 5 queries in sequence
# DuckDB: 0.3s + 0.2s + 0.5s + 0.3s + 0.4s = 1.7s total
# Spark: 8s + 8s + 9s + 8s + 9s = 42s total
# Users will not wait 42 seconds for each agent response

Cuándo Gana Spark

1. Los Datos Exceden la Memoria de Una Sola Máquina (> 200GB)

Cuando tu dataset es más grande de lo que cabe en RAM, Spark distribuye el trabajo a través de un cluster:

# 2TB dataset — Spark distributes across 20 nodes
spark.sql("""
SELECT user_id, SUM(amount) as lifetime_value
FROM massive_events -- 2TB, 10 billion rows
GROUP BY user_id
HAVING lifetime_value > 1000
""").write.parquet("s3://bucket/gold/high_value_users/")

2. Pipelines ETL de Producción

La tolerancia a fallos de Spark (linaje de RDD, ejecución especulativa, reintento de tareas) lo hace confiable para trabajos de producción programados:

# Daily pipeline that processes yesterday's data
# If a node dies mid-job, Spark retries the failed tasks
spark.sql("""
INSERT OVERWRITE TABLE gold_daily_metrics
SELECT DATE(event_time) as day, COUNT(*) as events
FROM raw_events
WHERE DATE(event_time) = CURRENT_DATE - 1
GROUP BY day
""")

3. Machine Learning a Escala

Spark MLlib maneja entrenamiento distribuido en datasets masivos:

from pyspark.ml.classification import RandomForestClassifier
from pyspark.ml.feature import VectorAssembler
# Train on 1 billion rows — distributed across cluster
assembler = VectorAssembler(inputCols=feature_cols, outputCol="features")
rf = RandomForestClassifier(numTrees=100, maxDepth=10)
pipeline = Pipeline(stages=[assembler, rf])
model = pipeline.fit(training_data) # distributed training

4. Streaming

Spark Structured Streaming maneja ingesta y procesamiento de datos en tiempo real:

stream = spark.readStream \
.format("kafka") \
.option("kafka.bootstrap.servers", "kafka:9092") \
.option("subscribe", "events") \
.load()
# Process in real-time, write to data lake
stream.writeStream \
.format("parquet") \
.option("path", "s3://bucket/raw/events/") \
.option("checkpointLocation", "s3://bucket/checkpoints/events/") \
.start()

DuckDB no hace streaming. Es un motor de consultas batch.

La Apuesta de DataSpoc: DuckDB para el 90% de los Equipos

La mayoría de las empresas tienen data lakes de menos de 100GB. La mayoría de las consultas analíticas son ad-hoc: alguien hace una pregunta, un analista escribe SQL, la respuesta regresa. Para esta carga de trabajo, DuckDB es estrictamente mejor que Spark:

  • Más rápido para datasets menores a 100GB
  • Más barato por 10-50x (sin cluster que ejecutar)
  • Más simple de operar (sin JVM, sin YARN, sin configuración de cluster)
  • Mejor para agentes de IA (inicio instantáneo, consultas sub-segundo)
from dataspoc_lens import LensClient
lens = LensClient()
# This is all you need. No cluster. No JVM. No config.
df = lens.query("SELECT region, SUM(amount) FROM curated_sales GROUP BY region")
print(df)

Si superas DuckDB — tu lake pasa de 200GB, necesitas streaming, necesitas ML distribuido — Spark está ahí. Los datos siguen siendo Parquet en un bucket cloud. Cambiar motores de consulta no requiere migrar datos.

Esa es la belleza del formato abierto: a Parquet no le importa qué lo lee. Empieza con DuckDB. Gradúate a Spark si y cuando lo necesites. La mayoría de los equipos nunca lo necesitarán.

Marco de Decisión Rápida

Is your data lake > 200GB?
→ Yes: Consider Spark
→ No: Use DuckDB (DataSpoc Lens)
Do you need real-time streaming?
→ Yes: Spark Structured Streaming
→ No: DuckDB
Are you building production ETL that runs 24/7?
→ Yes: Spark (fault tolerance matters)
→ No: DuckDB
Are you doing ad-hoc analysis or AI agent queries?
→ Yes: DuckDB (speed matters)
→ No: Either works
What is your infrastructure budget?
→ $0-100/month: DuckDB (no cluster needed)
→ $500+/month: Spark is viable

Para el 90% de los equipos de datos, la respuesta es DuckDB. DataSpoc Lens lo hace sin esfuerzo.

Recomendados