DuckDB: O banco de dados para ciência de dados

Pedro Holanda
Data Hackers
Published in
6 min readDec 12, 2022

O DuckDB é um banco de dados embarcado, colunar e relacional projetado para ser rápido e fácil de usar em aplicações de ciência de dados. É uma ferramenta open-source que utiliza a licença do MIT. O sistema em si, é escrito em C++, mas apresenta APIs para diversas linguagens, entre elas Python, R, C, Rust, Java, JS. Também possui extenso suporte a SQL.

Atualmente, o projeto consiste em mais de 7600 estrelas no Github e possui mais de 700.000 downloads por mês no seu módulo em Python.

As principais características do DuckDB são:

  1. Usabilidade. Diferente da grande maioria dos outros sistemas de banco de dados, o DuckDB não necessita de uma instância de um servidor. Por ser um sistema embarcado, não existe nenhuma operação adicional para iniciar um Banco. Além disso, o DuckDB consegue ler arquivos CSV/Parquet sem criação de esquema e nenhum custo adicional.
-- Essa consulta automaticamente infere tipos e formato de um arquivo CSV
SELECT * FROM 'path/file.csv'

2. Otimizações para análise de dados. O DuckDB implementa o estado da arte em técnicas de análise de dados. Como formato colunar, processamento vetorizado, achatamento de subconsultas.

3. Paralelismo. A grande parte dos operadores de consulta do DuckDB tem versões paralelas, garantindo a utilização máxima do poder de processamento da máquina.

4. Execução além da memória. Os operadores do DuckDB conseguem fazer caching dos dados em memória e substituir com dados do disco durante a execução, garantindo que consultas onde dos dados não cabem em memória serão corretamente finalizadas.

5. Compressão. O estado da arte em compressão de strings, floating point e inteiros é implementado no sistema de armazenamento do DuckDB. Dados como a tabela lineitem do TPC-H e os dados de corridas de táxi de Nova Iorque chegam a atingir 5x menos espaço em disco quando comprimidas.

DuckDB no ecosistema do Python

O DuckDB possui integrações com outras bibliotecas do Python. Como, por exemplo, o Pandas, NumPy, PyArrow. Essas integrações permitem que usuários consigam ler e escrever dados para essas bibliotecas sem nenhum custo de cópia. Isso é possível por conta da proximidade de como os dados são representados no DuckDB e em outras bibliotecas de análise de dados.

  1. Pandas
import pandas as pd
import duckdb

# Criação de um dataframe
d = {'col1': [1,2], 'col2': [3,4]}
df = pd.DataFrame(data=d)

# Criação de uma conexão do DuckDB
con = duckdb.connect()
# Leitura dos dados armazenados na variável df, sem realização de cópia
res = con.execute("SELECT * FROM df")
# Produção do resultado da consulta para um dataframe do pandas, novamente
# sem cópia
res_df = res.df()

2. PyArrow

import pyarrow as pa
import duckdb

# Criação de uma tabela arrow
d = {'col1': [1,2], 'col2': [3,4]}
arrow = pa.Table.from_pydict(d)

# Criação de uma conexão do DuckDB
con = duckdb.connect()
# Leitura dos dados armazenados na variável arrow, sem realização de cópia
res = con.execute("SELECT * FROM arrow")
# Produção do resultado da consulta para uma tabela arrow, novamente
# sem cópia
res_df = res.arrow()

O DuckDB também apresenta duas APIs diferentes em Python.

  1. DB API 2.0. Segue a especificação, usuários podem utilizar SQL para executar consultas.
import duckdb
# Criação de uma conexão que armazena dos dados no arquivo database.db
con = duckdb.connect('database.db')
# Executa a consulta e cria uma lista de tuplas com o resultado.
res = con.execute('SELECT j+1 FROM integers WHERE i = 2').fetchall()

2. API Relacional

import duckdb
con = duckdb.connect('database.db')

# Cria o objeto tabela que referencia a tabela integers
tbl = duckdb.table('integers')

# Executa a consulta encadeando operadores, similar ao Pandas
tbl.filter('i=1').project('j+1').show()

Demo

Para o nosso demo, nós utilizaremos o banco de dados das corridas de carro de Nova Iorque, e responderemos se o valor da gorjeta aumenta, com o aumento do número de passageiros em um carro, para viagens menores que cinco milhas.

Para instalar o DuckDB, o Pandas e baixar o dataset:

pip install duckdb
pip install pandas
wget "https://d37ci6vzurychx.cloudfront.net/trip-data/yellow_tripdata_2016-01.parquet"

No código abaixo nós importamos a bibliotecas do Pandas e DuckDB. Nós lemos o arquivo parquet utilizando a função read_parquet do Pandas, que nos retorna um dataframe do Pandas. Note que se quisessemos poderiamos ler o arquivo diretamente com o DuckDB passando o caminho do arquivo na clausula FROM .

Em seguida executamos a consulta no DuckDB diretamente no dataframe do Pandas. Essa consulta retorna o número de passageiros e a média da gorjeta para corridas menores que 5 milhas. O resultado é um dataframe do Pandas. O que no permite utilizar a função plot.bar() para gerar uma figura de fácil interpretação.

O ponto principal desse demo é demonstrar que o DuckDB consegue operar com dados no Pandas e exportar um dataframe, para utilização de funções integradas com o Pandas. Assim, o usuário pode ter a desempenho do DuckDB enquanto usufrui da flexibilidade do Pandas e de outras bibliotecas que se integram com o Pandas (e.g., Tensorflow).

import pandas
import duckdb

# O arquivo é um arquivo parquet, para esse demo utilizaremos ele carregado
# em um dataframe do Pandas.
# Note que o mesmo é possível diretamente no DuckDB com
# SELECT * FROM 'yellow_tripdata_2016-01.parquet'
df = pandas.read_parquet('yellow_tripdata_2016-01.parquet')

# Executa a consulta que seleciona o número de passageiros e a média de gorgeta
# para corridas com menos de 5 milhas e agrupadas pelo núemro de passageiros.

con = duckdb.connect()
sql = """ SELECT passenger_count, avg(tip_amount) as tip_amount
FROM df
WHERE trip_distance < 5
GROUP BY passenger_count"""
# Transforma o resultado para um Dataframe do Pandas.
result_df = con.execute(sql).df()
# Utiliza a função plot.bar do pandas para produzir uma imagem.
result_df.plot.bar(x="passenger_count", y="tip_amount")
Resultado da função plot.bar

Desempenho

Abaixo nós temos a comparação entre o DuckDB e o Pandas quando executando a mesma consulta.

O código para o experimento pode ser encontrado aqui. Foi executado em um Macbook M1, 2020.

Note que nesse experimento, o DuckDB executa a consulta em um dataframe do Pandas e o resultado é também um dataframe.

A diferença é de aproximadamente 20x. Dentre as principais razões temos:

1. “Pushdown” automático dos operadores de projeção e filtro no DuckDB. Basicamente o DuckDB somente lê da tabela as colunas necessárias e já aplica o filtro trip_distance < 5 durante a execução da leitura. No Pandas esses operadores ocorrem após a leitura da tabela.

2. Algoritmo para agrupamento. O algoritmo de agrupamento do DuckDB só necessita de um passe sobre os dados para descobrir e criar os grupos. No caso do Pandas, é necessário ler os dados duas vezes, primeiro para achar os grupos distintos e em seguida para criar-los.

3. Paralelismo. Os operadores do DuckDB rodam em paralelo, nesse caso em 4 threads. No Pandas os operadores somente podem ser executados utilizando uma única thread.

4. Processamento Vetorizado. O DuckDB utiliza os caches da CPU de maneira mais efetiva do que o modelo de processamento em coluna utilizado pelo Pandas.

Conclusão

O DuckDB traz para o ecossistema do Python um sistema de banco de dados analítico, embarcado e sem o custo extra, comum em outros bancos relacionais, de se gerenciar um sistema.

Não só o DuckDB traz a possibilidade de executar consultas utilizando SQL. Como se integra com outras bibliotecas bastante utilizadas no ecossistema Python. Essa integração faz com que a adesão do DuckDB em projetos já existentes tenha um baixo custo, e permite que usuários utilizem o melhor de vários projetos.

No caso do Demo apresentado nesse artigo, usuários podem utilizar o moderno motor de execução do DuckDB, mantendo os dados em dataframes. Assim usufruindo de todas as outras ferramentas que se integram ao Pandas.

Sobre o Autor

Pedro Holanda é o Chief of Operations da DuckDB Labs. Empresa situada em Amsterdam que fornece serviços para o sistema de gerenciamento de dados DuckDB. Pedro também é Pós-Doutorando no grupo de Arquitetura de Banco de Dados, na CWI, também em Amsterdam.

--

--

Pedro Holanda
Data Hackers

I’m a Post-Doc based in Amsterdam and a member of the Database Architecture group at CWI. I’m currently working as COO @ DuckDB Labs.