¿Quieres procesar terabytes sin montar una base de datos gigante? Entonces escucha esto
Tiempo estimado de lectura: 6 min
- Conexión es arquitectura: la forma en que abres DuckDB define persistencia, permisos y paralelismo.
- Memoria vs archivo: usa :memory: para ETL temporal y archivo.duckdb para persistencia compartida con control de escrituras.
- No muevas datos innecesariamente: DuckDB puede leer Parquet/CSV directo desde disco o S3.
- Concurrencia: lectura concurrente OK; escritura concurrente NO — diseña un writer único o locking.
- Práctico: patrones para notebooks, CI, microservicios y agentes LLM.
Introducción
¿Quieres procesar terabytes sin montar una base de datos gigante? Entonces escucha esto: you must first create a connection to a database. Sí, suena obvio. Pero en DuckDB esa línea es la puerta de entrada y la que decide si tu proyecto será veloz o un desastre con estilo.
Voy a contarte cómo usar DuckDB desde esa perspectiva: la conexión no es un paso más. Es la arquitectura. Es la diferencia entre “funciona” y “funciona rápido y sin dramas”. Poca gente habla así. Aquí no hay postureo: te doy lo que sirve, cómo usarlo y qué evitar.
Resumen rápido (lectores con prisa)
DuckDB es un motor OLAP embebido similar a SQLite pero optimizado para análisis. Decide si usas :memory: (veloz, efímero) o un archivo .duckdb (persistente, cuidado con escrituras concurrentes). Lee Parquet/CSV directo desde disco y evita mover terabytes innecesariamente.
Qué es DuckDB y por qué la conexión importa
DuckDB es un motor OLAP embebido. Es como SQLite, pero para análisis masivo. Corre dentro del proceso, lee Parquet/CSV directo desde disco y procesa columnas en bloques vectorizados. No hablas por red con un servidor; abres una instancia dentro de tu app y le preguntas cosas.
Por eso la regla número uno (deletréalo): you must first create a connection to a database. Esa conexión define:
- si los datos sobreviven al reinicio (archivo .duckdb vs :memory:),
- quién puede escribir y quién leer,
- cuánto paralelismo y memoria va a usar tu proceso.
Cómo crear esa conexión (rápido y práctico)
Te doy recetas útiles. Párate un segundo en la que mejor encaje con tu stack.
Python
Instalación: pip install duckdb
Conexión en memoria y persistente:
import duckdb
# Conexión en memoria (efímera)
con = duckdb.connect(database=':memory:')
# Conexión persistente (archivo en disco)
con = duckdb.connect(database='mi_base.duckdb')
Node.js
Instalación: npm install duckdb
Conexión:
const duckdb = require('duckdb');
// Conexión a archivo (o ':memory:')
const db = new duckdb.Database(':memory:');
// Ejecutar SQL
db.run("CREATE TABLE prueba(a INTEGER, b VARCHAR)");
CLI (rápido para pruebas)
Desde terminal:
duckdb mi_base.duckdb
Y luego puedes correr SQL directo: SELECT * FROM read_parquet('datos.parquet');
Nota: hay bindings para Rust, Go, R y otros. La idea es la misma: you must first create a connection to a database y decidir si será en memoria o persistente.
Memoria vs persistencia: la decisión que cambia todo
No es trivia. Es estrategia.
:memory:
- Ventaja: velocidad máxima, útil para ETL temporal, análisis ad-hoc, notebooks y agentes LLM que generan SQL y necesitan respuesta inmediata.
- Desventaja: se pierde todo al cerrar el proceso.
archivo.duckdb
- Ventaja: persistencia; comparte estado entre ejecuciones (si lo permites).
- Desventaja: concurrencia de escritura limitada; hay locking si varios procesos escriben.
Regla práctica: si vas a procesar datos y luego descartarlos, usa memoria. Si necesitas guardar resultados para consultas posteriores, usa archivo. Pero si varios servicios escriben al mismo archivo, tendrás que diseñar una capa para evitar colisiones.
Leer archivos sin importar tablas
Aquí está la magia: no necesitas INSERTs. DuckDB puede consultar Parquet/CSV directamente desde la conexión.
SELECT category, SUM(sales) total
FROM read_parquet('s3://bucket/datos.parquet')
GROUP BY category;
Eso te salva horas y espacio. No muevas terabytes solo para filtrar una columna.
Patrones de uso según casos reales
- Notebook o análisis ad-hoc: conexión en memoria. Carga Parquet y explora con SQL.
- ETL temporal en CI: conexión en memoria dentro del runner. Resultado exportado a CSV o Parquet.
- Dashboard local o app de escritorio: archivo.duckdb para persistencia.
- Microservicio que solo lee: muchos procesos pueden leer el mismo archivo sin problema.
- Escrituras concurrentes: no pongas múltiples instancias a escribir en el mismo archivo. Mejor un microservicio responsable de escribir, o sincronización via colas.
Concurrencia: dónde pica y cómo evitar sangre
DuckDB está diseñado para lectura concurrente y un único escritor. Si dos procesos intentan escribir al mismo .duckdb, obtendrás locking y fallos.
Estrategias:
- Arquitectura con un escritor único: un servicio que reciba las operaciones de escritura y haga commits.
- Locking a nivel de aplicación: si no puedes, usa un lock distribuido (Redis, etcd) antes de abrir la conexión de escritura.
- Escribe a archivos temporales y luego mergea: cada proceso escribe su parquet y un job batch hace el merge en la base persistente.
Performance y ajustes que realmente importan
- Usa Parquet para entrada masiva: DuckDB lee columnas y se aprovecha de la compresión y esquema.
- Paralelismo: DuckDB puede usar múltiples threads. Ajusta
duckdb.set_config('threads', 'N')si tu binding lo permite. - PRAGMA options: hay settings para controlar memoria y spill-to-disk behavior.
- Evita
SELECT *en tablas enormes; especifica columnas. - Usa índices? DuckDB no usa índices tradicionales como OLTP; piensa en particionado y ordenamiento de input para lecturas eficientes.
Integración con flujos de automatización y agentes LLM
Si tu stack tiene n8n, Airbyte, o agentes que generan SQL dinámicamente, DuckDB es ideal.
Ejemplo de flujo:
- Nodo descarga CSV desde S3.
- Nodo invoca DuckDB en memoria y ejecuta SQL para join y agregación.
- Nodo exporta resultado a Parquet y sube a S3.
Para agentes LLM que responden preguntas sobre datos locales: el agente genera SQL, se ejecuta contra DuckDB en memoria, devuelve un resultado y el agente responde en lenguaje natural. Rápido, barato y reproducible.
Errores comunes que verás (y cómo arreglarlos)
- “Database is locked” → intentaste escribir desde dos procesos. Solución: un unico writer o locking.
- Permisos de archivo → chequea permisos OS; el proceso necesita RW.
- Tropiezos con dependencias nativas en containers → usa la imagen oficial o instala dependencias del sistema.
- Diferencias entre entornos (dev vs prod) → recuerda que
:memory:no persiste; prueba en un archivo en staging.
Checklist de buenas prácticas (hazlo ya)
- Decide: memoria o persistente.
- Si persistente, asegúrate de tener backup y control de versiones del archivo.
- Para producción, centraliza escrituras o usa locking.
- Mide: tiempos de query, uso de memoria y duración de tasks en CI.
- Usa Parquet para input masivo.
- Añade tests E2E que incluyan consultas típicas.
- Documenta el esquema emergente: DuckDB permite crear tablas, pero si tu pipeline escribe Parquet, documenta columnas y tipos.
Un par de ejemplos prácticos (copy-paste listos)
Python: consulta Parquet en memoria y guarda resultado
import duckdb
con = duckdb.connect(':memory:')
res = con.execute("""
SELECT user_id, SUM(amount) AS total
FROM read_parquet('datos.parquet')
WHERE date >= '2024-01-01'
GROUP BY user_id
""").fetchdf()
res.to_parquet('resultados.parquet')
con.close()
Node.js: abrir archivo y ejecutar query
const duckdb = require('duckdb');
const db = new duckdb.Database('analitica.duckdb');
db.all("SELECT COUNT(*) as c FROM read_csv_auto('grande.csv')", (err, rows) => {
if (err) throw err;
console.log(rows);
});
Metáfora que ayuda a decidir
Piensa en DuckDB como una navaja suiza para análisis: pequeña, compacta, infinitamente útil en el bolsillo. Pero no pretendas que sea la planta de acero que alimenta toda la ciudad. Para eso hay sistemas distribuidos. DuckDB es la herramienta que te permite resolver 80% de problemas analíticos sin desplegar infraestructura industrial.
¿Quieres ponerlo en producción sin dramas?
Haz esto primero:
- Prototipa en memoria.
- Pasa a archivo en staging.
- Simula carga concurrente de lectura/escritura.
- Implementa el patrón de writer único si hace falta.
- Monitorea latencia y uso de disco/CPU.
Cierre y llamada a la acción: You must first create a connection to a database. Pero crearla no basta: decide bien, prueba y diseña para concurrencia.
Si quieres, te escribo un script que:
- comprueba Node/Python environment,
- crea un archivo .duckdb de ejemplo,
- carga un Parquet y corre consultas de benchmarking,
- y genera un reporte JSON con tiempos y memoria.
Respóndeme con “QUIERO DUCK” y te lo preparo en Python o Node.js en 5 minutos. No es magia, es evitar noches de deploy. Y si quieres que lo haga en tu repo, pásame el enlace y te doy un plan de 3 commits para poner DuckDB sin romper nada.
Dominicode Labs
Para flujos de automatización, agentes y workflows aplicados al manejo de datos y DuckDB, revisa recursos y experimentos en Dominicode Labs. Es una continuación práctica para integrar patrones de escritor único, locking y pruebas de concurrencia en pipelines reales.
FAQ
- ¿Qué significa “you must first create a connection to a database” en la práctica?
- ¿Cuándo debo usar :memory: versus un archivo .duckdb?
- ¿Puedo escribir desde varios procesos al mismo archivo .duckdb?
- ¿DuckDB puede leer datos desde S3 sin descargarlos?
- ¿Cómo ajusto el paralelismo en DuckDB?
- ¿Qué hago si veo “Database is locked”?
- ¿Es DuckDB adecuado para dashboards en producción?
Es la instrucción que abre el motor dentro de tu proceso. Define si la sesión es efímera (:memory:) o persistente (archivo), quién puede leer/escribir y qué recursos usará.
Usa :memory: para análisis temporales, notebooks y CI donde no necesitas persistencia. Usa un archivo .duckdb si necesitas conservar resultados entre ejecuciones o compartir estado con control de accesos.
No directamente. DuckDB permite lectura concurrente, pero la escritura concurrente al mismo archivo provoca locking. Diseña un writer único o un mecanismo de locking en la aplicación.
Sí. DuckDB puede leer Parquet/CSV desde rutas S3 usando funciones como read_parquet('s3://bucket/datos.parquet'), evitando mover datos innecesariamente.
Ajusta el número de threads si tu binding lo soporta, por ejemplo duckdb.set_config('threads', 'N'), y usa PRAGMA/settings para controlar memoria y comportamiento de spill-to-disk.
Significa que hubo un intento de escritura concurrente. Solución: evita múltiples escritores, implementa un writer único o usa un lock distribuido antes de escribir.
Sí, para dashboards locales o apps de escritorio es una buena opción. Para entornos con múltiples escritores simultáneos en producción, diseña un patrón de escritura centralizado.
