Lewati ke konten utama

DuckDB untuk Data Engineer: Percepat Pipeline Data Anda 10x atau Lebih Cepat

DuckDB adalah mesin analitik yang andal yang berjalan di laptop Anda. Anda dapat menggunakannya untuk mempercepat pembacaan dan pemrosesan data serta memangkas runtime pipeline dari menit menjadi detik. Ikuti panduan praktis ini untuk mempelajarinya.
Diperbarui 28 Sep 2026  · 13 mnt Baca

Jelajahi bersama AI

ChatGPTClaudePerplexity

Sudah berapa kali manajer menanyakan berapa lama pipeline Anda berjalan, dan Anda merasa canggung menjawabnya?

Anda tidak sendiri. Kenyamanan Python ada harganya. Biasanya, biayanya ada pada kecepatan runtime. Pipeline mudah ditulis tetapi sulit diskalakan, sering kali memakan waktu semakin lama seiring meningkatnya kebutuhan proyek.

Namun mungkin bukan kode Anda yang menjadi hambatannya. Mungkin tidak ada lagi performa yang bisa Anda peras dari pandas. Mungkin beralih ke mesin pemrosesan data lain dapat memangkas runtime dari jam menjadi menit.

Di situlah DuckDB berperan.

Dalam artikel ini, saya akan menjelaskan apa itu DuckDB dan mengapa ini penting bagi data engineer. Anda akan mempelajari cara menggunakan DuckDB melalui contoh langsung dan melihat seberapa jauh lebih cepat dibandingkan dengan pustaka pemrosesan data paling populer di Python. 

Apa itu DuckDB, dan Apakah Ini Akan Menjadi Tren Besar Berikutnya untuk Data Engineer?

DuckDB adalah DBMS OLAP relasional bersifat embedded, in-process, dan sumber terbuka.

Secara sederhana, anggaplah ini sebagai database kolumnar analitik yang berjalan di memori. Sebagai database analitik, ini dioptimalkan untuk pernyataan SELECT, bukan INSERT dan UPDATE. Mirip SQLite, tetapi kebalikannya.

DuckDB sudah ada sekitar 5 tahun, tetapi rilis “stabil” pertama diumumkan pada Juni 2024 — 3 bulan lalu saat artikel ini ditulis. Namun jangan tertipu oleh faktor kebaruannya — DuckDB telah banyak dicoba dan diuji, dan hampir selalu direkomendasikan saat kecepatan menjadi prioritas.

Mengapa memilih DuckDB untuk pipeline data Anda

Jika Anda seorang data engineer, berikut beberapa manfaat konkret menggunakan DuckDB dalam pipeline data Anda:

  • Cepat: Pikirkan perbedaan berlipat-lipat dibandingkan pustaka data frame bawaan Python. Dalam banyak kasus, bahkan lebih cepat daripada pustaka yang dioptimalkan untuk kecepatan (misalnya, polars).
  • Sumber terbuka: Seluruh kode sumber tersedia di repo GitHub publik. Anda dapat menggunakannya, menyesuaikannya, dan bahkan berkontribusi pada proyeknya.
  • Menunda kebutuhan pindah ke cloud: Komputasi cloud bisa mahal, tetapi biayanya sering kali sepadan saat kecepatan pemrosesan sangat krusial. Dengan DuckDB, Anda bisa menganalisis ratusan juta baris di laptop Anda.
  • Mudah dipelajari: Memulai dengan DuckDB itu mudah. Anda mungkin hanya perlu perubahan minimal pada pipeline data yang sudah ada, karena DuckDB terintegrasi baik dengan pustaka pemrosesan data Python.
  • Integrasi mulus dengan platform penyimpanan cloud: Misalnya, Anda dapat menulis kueri SQL DuckDB yang membaca data langsung dari AWS S3. Tidak perlu mengunduh datanya terlebih dahulu.

Untuk alasan-alasan ini (dan masih banyak lagi), saya rasa DuckDB adalah lompatan besar berikutnya bagi data engineer.

Cara Memulai dengan DuckDB

Seperti yang saya sebutkan, DuckDB berjalan di laptop Anda. Langkah pertama adalah memasangnya di sana. 

Saya akan menunjukkan langkah instalasi untuk macOS, tetapi petunjuk untuk Windows dan Linux sudah dijelaskan dengan baik dan mudah diikuti.

Langkah 1: Instal DuckDB

Jika Anda menggunakan Mac, cara termudah memasang DuckDB adalah dengan Homebrew. Jalankan perintah shell berikut:

brew install duckdb

Dalam hitungan detik, Anda akan melihat keluaran seperti berikut:

Menginstal DuckDB dengan Homebrew

Menginstal DuckDB dengan Homebrew

Setelah terpasang, Anda dapat masuk ke shell DuckDB dengan perintah berikut:

duckdb

Shell DuckDB

Shell DuckDB

Dari sini, dunia ada di tangan Anda!

Anda dapat menulis dan menjalankan kueri SQL langsung dari konsol. Misalnya, saya telah mengunduh data NYC Taxi enam bulan pertama tahun 2024. Jika Anda melakukan hal yang sama, gunakan cuplikan berikut untuk menampilkan jumlah baris dari satu file Parquet:

SELECT COUNT(*)
  FROM PARQUET_SCAN("path-to-data.parquet");

Jumlah baris file Parquet tunggal

Jumlah baris file Parquet tunggal

Benar — itu hampir 20 juta baris dari satu file. Saya mengunduh 24 file.

Anda mungkin tidak ingin menjalankan DuckDB dari terminal, jadi berikutnya, saya akan memandu Anda menyambungkan DuckDB dengan Python.

Langkah 2: Sambungkan DuckDB ke alur kerja Python Anda

Tujuan bagian ini adalah menunjukkan cara mengonfigurasi dan menjalankan DuckDB dalam bahasa paling populer untuk data engineering: Python.

Bahkan, DataCamp menawarkan jalur karier lengkap untuk data engineering dengan Python.

Python memiliki pustaka DuckDB khusus yang perlu Anda instal terlebih dahulu. Jalankan perintah berikut dari terminal, sebaiknya di lingkungan virtual:

pip install duckdb

Sekarang buat file Python baru dan tempelkan kode berikut:

import duckdb
# Function to get the count from a single file
def get_row_count_from_file(conn: duckdb.DuckDBPyConnection, file_path: str) -> int:
    query = f"""
        SELECT COUNT(*)
        FROM PARQUET_SCAN("{file_path}")
    """
    return conn.sql(query).fetchone()[0]
if __name__ == "__main__":
    # In-memory database connection
    conn = duckdb.connect()
    # Path to a parquet file
    file_path = "fhvhv_tripdata_2024-01.parquet"
    # Get the row count
    row_count = get_row_count_from_file(conn=conn, file_path=file_path)
    print(row_count)

Singkatnya, cuplikan kode ini memiliki fungsi yang mengambil jumlah baris dari satu file, dengan memberikan koneksi DuckDB yang valid dan path file.

Anda akan mendapatkan hasil berikut hampir seketika setelah menjalankan skrip:

Koneksi DuckDB dan Python

Koneksi DuckDB dan Python

Itu saja! 

Selanjutnya, saya akan menunjukkan cara melakukan hal-hal hebat dengan Python dan DuckDB dengan sangat cepat!

DuckDB dalam Aksi: Cara Mempercepat Pipeline Data

Sebagai acuan, saya akan menjalankan kode pada bagian 2024 (Januari - Juni) dari Dataset NYC Taxi — khusus untuk kendaraan for-hire ber-volume tinggi. Itu berarti 6 file Parquet, berukuran sekitar 3GB ruang disk. Untuk perangkat keras, saya menggunakan MacBook Pro 16” M3 Pro dengan 12 core CPU dan 36GB unified memory.

Hasil Anda bisa berbeda, tetapi seharusnya masih berada di kisaran yang mirip.

Membaca dataset besar dalam waktu singkat

Saya telah mengonversi 6 file ini ke dua format tambahan — CSV dan JSON. Anda dapat melihat berapa banyak ruang disk yang dibutuhkan masing-masing:

Perbandingan ukuran dataset

Perbandingan ukuran dataset

Total: 2,96GB untuk Parquet, 19,31GB untuk CSV, dan 66,04GB untuk JSON.

Perbedaannya besar! Jika tidak ada hal lain yang Anda ingat dari artikel hari ini, setidaknya ingatlah untuk selalu menggunakan format file Parquet saat menangani file besar. Ini akan menghemat waktu pemrosesan dan ruang disk.

DuckDB memiliki fungsi praktis yang dapat membaca beberapa file dengan format yang sama sekaligus (menggunakan pola glob). Saya akan menggunakannya untuk membaca keenamnya guna mendapatkan jumlah baris dan membandingkan runtime:

import duckdb
import pandas as pd
from datetime import datetime
def get_row_count_and_measure_time(file_format: str) -> str:
    # Construct a DuckDB query based on the file_format
    match file_format:
        case "csv":
            query = """
            SELECT COUNT(*)
            FROM READ_CSV("nyc-taxi-data-csv/*.csv")
            """
        case "json":
            query = """
            SELECT COUNT(*)
            FROM READ_JSON("nyc-taxi-data-json/*.json")
            """
        case "parquet":
            query = """
                SELECT COUNT(*)
                FROM READ_PARQUET("nyc-taxi-data/*.parquet")
            """
        case _:
            raise KeyError("Param file_format must be in [csv, json, parquet]")
    # Open the database connection and measure the start time
    time_start = datetime.now()
    conn = duckdb.connect()
    # Get the row count
    row_count = conn.sql(query).fetchone()[0]
    # Close the database connection and measure the finish time
    conn.close()
    time_end = datetime.now()
    return {
        "file_format": file_format,
        "duration_seconds": (time_end - time_start).seconds + ((time_end - time_start).microseconds) / 1000000,
        "row_count": row_count
    }
# Run the function
file_format_results = []
for file_format in ["csv", "json", "parquet"]:
    file_format_results.append(get_row_count_and_measure_time(file_format=file_format))
pd.DataFrame(file_format_results)

Hasilnya sudah ada — pemenangnya jelas hanya satu:

Perbandingan runtime pembacaan file

Perbandingan runtime pembacaan file

Berkat DuckDB, ketiganya cepat dan berakhir di tempat yang sama, tetapi jelas Parquet menang dengan faktor 600 dibanding CSV dan dengan faktor 1200 dibanding JSON.

Untuk alasan ini, saya hanya akan menggunakan Parquet untuk sisa artikel.

Kueri data dengan SQL

DuckDB memungkinkan Anda mengagregasikan data melalui SQL.

Tujuan saya di bagian ini adalah mengembangkan gagasan tersebut. Lebih tepatnya, saya akan menunjukkan cara menghitung statistik ringkasan pada level bulanan yang mencakup informasi tentang jumlah perjalanan, waktu perjalanan, jarak perjalanan, biaya perjalanan, dan bayaran pengemudi—semuanya dari 120 juta baris data.

Bagian terbaiknya: Hanya butuh dua detik!

Ini adalah kode yang saya gunakan di dalam skrip Python untuk mengagregasikan data dan mencetak hasilnya:

conn = duckdb.connect()
query = """
    SELECT
        ride_year || '-' || ride_month AS ride_period,
        COUNT(*) AS num_rides,
        ROUND(SUM(trip_time) / 86400, 2) AS ride_time_in_days,
        ROUND(SUM(trip_miles), 2) AS total_miles,
        ROUND(SUM(base_passenger_fare + tolls + bcf + sales_tax + congestion_surcharge + airport_fee + tips), 2) AS total_ride_cost,
        ROUND(SUM(driver_pay), 2) AS total_rider_pay
    FROM (
        SELECT
            DATE_PART('year', pickup_datetime) AS ride_year,
            DATE_PART('month', pickup_datetime) AS ride_month,
            trip_time,
            trip_miles,
            base_passenger_fare,
            tolls,
            bcf,
            sales_tax,
            congestion_surcharge,
            airport_fee,
            tips,
            driver_pay
        FROM PARQUET_SCAN("nyc-taxi-data/*.parquet")
        WHERE 
            ride_year = 2024
        AND ride_month >= 1 
        AND ride_month <= 6
    )
    GROUP BY ride_period
    ORDER BY ride_period
"""
# Aggregate data, print it, and show the data type
results = conn.sql(query)
results.show()
print(type(results))

Keluaran berisi hasil agregasi, tipe variabel untuk results, dan total runtime:

Agregasi statistik ringkasan bulanan

Agregasi statistik ringkasan bulanan

Renungkan sejenak: Itu 3GB data dengan lebih dari 120 juta baris, semuanya diagregasikan dalam 2 detik di laptop!

Satu-satunya masalah adalah tipe variabelnya agak kurang berguna untuk jangka panjang. Untungnya, ada solusi mudah.

Integrasi dengan pandas dan DataFrame

Kelebihan menggunakan DuckDB bukan hanya cepat, tetapi juga terintegrasi baik dengan pustaka data frame favorit Anda: pandas.

Jika Anda memiliki himpunan hasil sementara seperti yang saya tunjukkan di atas, Anda hanya perlu memanggil metode .df() untuk mengonversinya menjadi DataFrame pandas:

# Convert to Pandas DataFrame
results_df = results.df()
# Print the type and contents
print(type(results_df))
results_df

Konversi DuckDB ke pandas

Konversi DuckDB ke pandas

Demikian pula, Anda dapat menggunakan DuckDB untuk melakukan perhitungan pada DataFrame pandas yang sudah dimuat ke memori.

Kuncinya adalah merujuk nama variabel setelah kata kunci FROM dalam kueri SQL DuckDB. Berikut contohnya:

# Pandas DataFrame
pandas_df = pd.read_parquet("nyc-taxi-data/fhvhv_tripdata_2024-01.parquet")
# Run SQL queries through DuckDB
duckdb_res = duckdb.sql("""
    SELECT
        pickup_datetime,
        dropoff_datetime,
        trip_miles,
        trip_time,
        driver_pay
    FROM pandas_df
    WHERE trip_miles >= 300
""").df()
duckdb_res

Kueri DuckDB pada DataFrame Pandas yang ada

Kueri DuckDB pada DataFrame pandas yang sudah ada

Kesimpulannya, Anda dapat sama sekali menghindari pandas atau melakukan agregasi lebih cepat pada DataFrame pandas yang ada.

Berikutnya, saya akan menunjukkan beberapa fitur DuckDB tingkat lanjut.

DuckDB Lanjutan untuk Data Engineer

Ada banyak hal tentang DuckDB yang tidak terlihat di permukaan.

Di bagian ini, saya akan memandu Anda melalui beberapa fitur DuckDB tingkat lanjut yang esensial bagi data engineer.

Ekstensi

DuckDB memungkinkan Anda memperluas fungsionalitas bawaannya melalui ekstensi inti dan komunitas extensions. Saya sering menggunakan ekstensi untuk terhubung ke sistem penyimpanan cloud dan database serta bekerja dengan format file tambahan.

Anda dapat memasang ekstensi melalui konsol Python maupun DuckDB.

Pada cuplikan kode di bawah, saya menunjukkan cara memasang ekstensi httpfs yang diperlukan untuk terhubung ke AWS dan membaca data S3:

import duckdb
conn = duckdb.connect()
conn.execute("""
    INSTALL httpfs;
    LOAD httpfs;
""")

Anda tidak akan melihat keluaran apa pun jika tidak merantai metode .df() ke conn.execute(). Jika Anda melakukannya, Anda akan melihat pesan “Success” atau “Error”.

Kueri penyimpanan cloud

Sering kali, perusahaan Anda akan memberi akses ke data yang harus Anda masukkan ke pipeline. Biasanya data ini disimpan pada platform cloud yang skalabel, seperti AWS S3.

DuckDB memungkinkan Anda terhubung ke S3 (dan platform lain) secara langsung.

Saya sudah menunjukkan cara memasang ekstensi (httpfs), dan satu-satunya yang tersisa adalah mengonfigurasinya. AWS mengharuskan Anda memberikan informasi region, access key, dan secret access key.

Dengan asumsi Anda memiliki semuanya, jalankan perintah berikut melalui Python:

conn.execute("""    CREATE SECRET aws_s3_secret (
        TYPE S3,
        KEY_ID '<your-access-key>',
        SECRET '<your-secret-key>',
        REGION '<your-region>'
    );
""")

Bucket S3 saya berisi dua file dari dataset New York Taxi:

Isi bucket S3

Isi bucket S3

Tidak perlu mengunduh file, karena Anda dapat memindainya langsung dari S3:

import duckdb
conn = duckdb.connect()
aws_count = conn.execute("""
    SELECT COUNT(*)
    FROM PARQUET_SCAN('s3://<your-bucket-name>/*.parquet');
""").df()
aws_count

Hasil fetch DuckDB S3

Hasil fetch DuckDB S3

Anda tidak salah baca: Hanya butuh 4 detik untuk menelusuri ~ 900MB data yang berada di bucket S3.

Pemrosesan paralel

Dalam hal paralelisasi, DuckDB menerapkannya secara default berdasarkan row group: partisi data horizontal yang biasanya Anda temukan di Parquet. Satu row group dapat memiliki maksimum 122.880 baris. 

Paralelisme di DuckDB dimulai saat Anda bekerja dengan lebih dari 122.880 baris.

DuckDB akan otomatis meluncurkan thread baru saat itu terjadi. Secara default, jumlah thread sama dengan jumlah core CPU. Namun Anda tetap bisa mengatur jumlah thread secara manual.

Di bagian ini, saya akan menunjukkan caranya dan dampak pasti dari jumlah thread yang berbeda pada beban kerja yang identik.

Untuk melihat jumlah thread saat ini, jalankan perintah berikut:

SELECT current_setting('threads') AS threads;

Jumlah thread saat ini yang digunakan oleh DuckDB

Jumlah thread saat ini yang digunakan oleh DuckDB

Anda bisa mendapatkan hasil yang sama melalui Python.

Anda akan ingin menjalankan perintah SET threads = N untuk mengubah jumlah thread. 

Pada cuplikan kode berikut, saya akan menunjukkan cara menerapkan fungsi Python kustom yang menjalankan kueri DuckDB pada jumlah thread tertentu, mengonversinya menjadi DataFrame pandas, dan mengembalikan runtime (dan informasi lainnya). Kode di bawah fungsi menjalankannya pada rentang thread [1, 12]:

def thread_test(n_threads: int) -> dict:
    # Open the database connection and measure the start time
    time_start = datetime.now()
    conn = duckdb.connect()
    # Set the number of threads
    conn.execute(f"SET threads = {n_threads};")
    query = """
        SELECT
            ride_year || '-' || ride_month AS ride_period,
            COUNT(*) AS num_rides,
            ROUND(SUM(trip_time) / 86400, 2) AS ride_time_in_days,
            ROUND(SUM(trip_miles), 2) AS total_miles,
            ROUND(SUM(base_passenger_fare + tolls + bcf + sales_tax + congestion_surcharge + airport_fee + tips), 2) AS total_ride_cost,
            ROUND(SUM(driver_pay), 2) AS total_rider_pay
        FROM (
            SELECT
                DATE_PART('year', pickup_datetime) AS ride_year,
                DATE_PART('month', pickup_datetime) AS ride_month,
                trip_time,
                trip_miles,
                base_passenger_fare,
                tolls,
                bcf,
                sales_tax,
                congestion_surcharge,
                airport_fee,
                tips,
                driver_pay
            FROM PARQUET_SCAN("nyc-taxi-data/*.parquet")
            WHERE 
                ride_year = 2024
            AND ride_month >= 1 
            AND ride_month <= 6
        )
        GROUP BY ride_period
        ORDER BY ride_period
    """
    # Convert to DataFrame
    res = conn.sql(query).df()
    # Close the database connection and measure the finish time
    conn.close()
    time_end = datetime.now()
    return {
        "num_threads": n_threads,
        "num_rows": len(res),
        "duration_seconds": (time_end - time_start).seconds + ((time_end - time_start).microseconds) / 1000000
    }
thread_results = []
for n_threads in range(1, 13):
    thread_results.append(thread_test(n_threads=n_threads))
pd.DataFrame(thread_results)

Anda akan melihat keluaran yang mirip dengan milik saya:

Runtime DuckDB untuk jumlah thread yang berbeda

Runtime DuckDB untuk jumlah thread yang berbeda

Secara umum, semakin banyak thread yang Anda gunakan untuk suatu komputasi, semakin cepat selesai. Mungkin ada titik di mana overhead pembuatan thread baru justru meningkatkan total runtime, tetapi saya tidak menemukannya dalam rentang ini.

Perbandingan Performa: DuckDB vs Pendekatan Tradisional

Di bagian ini, saya akan menunjukkan cara menulis pipeline data dari nol. Ini akan relatif sederhana: membaca data dari disk, melakukan agregasi, dan menulis hasilnya. Tujuan saya adalah menunjukkan seberapa besar performa yang bisa Anda dapatkan dengan beralih dari pandas ke DuckDB. Selain itu, kode Anda juga akan terlihat lebih bersih.

Ini bukan pengantar komprehensif ke proses ETL/ELT melainkan gambaran tingkat tinggi.

Tujuan pipeline data

Pipeline data yang akan saya tunjukkan mencapai hal-hal berikut:

  • Extract: Membaca beberapa file Parquet dari disk (sekitar 120 juta baris).
  • Transform: Menghitung statistik ringkasan bulanan, seperti pendapatan perusahaan taksi, margin pendapatan (selisih biaya perjalanan dan bayaran pengemudi), dan rata-rata pendapatan per perjalanan. Anda juga akan melihat statistik umum lain yang dibahas sebelumnya.
  • Load: Menyimpan statistik bulanan secara lokal ke file CSV.

Setelah menjalankan kodenya, Anda akan mendapatkan hasil agregasi yang persis sama untuk kedua implementasi pipeline (mengabaikan sedikit perbedaan pembulatan):

Hasil pipeline untuk DuckDB dan Pandas

Hasil pipeline untuk DuckDB dan pandas

Pertama, mari kita bahas implementasi pipeline di pandas.

Kode: Python dan pandas

Apa pun yang saya coba, saya tidak bisa memproses keenam file Parquet sekaligus. Pesan peringatan sistem seperti ini muncul dalam hitungan detik:

Galat memori sistem

Galat memori sistem

Tampaknya 36GB memori tidak cukup untuk memproses 120 juta baris sekaligus. Akibatnya, skrip Python dihentikan:

Skrip Python dihentikan karena memori tidak mencukupi

Skrip Python dihentikan karena memori tidak mencukupi

Untuk mengatasinya, saya harus memproses file Parquet secara berurutan. Berikut kode yang saya gunakan untuk pipeline data:

import os
import pandas as pd
def calculate_monthly_stats_per_file(file_path: str) -> pd.DataFrame:
    # Read a single Parquet file
    df = pd.read_parquet(file_path)
    # Extract ride year and month
    df["ride_year"] = df["pickup_datetime"].dt.year
    df["ride_month"] = df["pickup_datetime"].dt.month
    # Remove data points that don"t fit in the time period
    df = df[(df["ride_year"] == 2024) & (df["ride_month"] >= 1) & (df["ride_month"] <= 6)]
    # Combine ride year and month
    df["ride_period"] = df["ride_year"].astype(str) + "-" + df["ride_month"].astype(str)
    # Calculate total ride cost
    df["total_ride_cost"] = (
        df["base_passenger_fare"] + df["tolls"] + df["bcf"] +
        df["sales_tax"] + df["congestion_surcharge"] + df["airport_fee"] + df["tips"]
    )
    # Aggregations
    summary = df.groupby("ride_period").agg(
        num_rides=("pickup_datetime", "count"),
        ride_time_in_days=("trip_time", lambda x: round(x.sum() / 86400, 2)),
        total_miles=("trip_miles", "sum"),
        total_ride_cost=("total_ride_cost", "sum"),
        total_rider_pay=("driver_pay", "sum")
    ).reset_index()
    # Additional attributes
    summary["total_miles_in_mil"] = summary["total_miles"] / 1000000
    summary["company_revenue"] = round(summary["total_ride_cost"] - summary["total_rider_pay"], 2)
    summary["company_margin"] = round((1 - (summary["total_rider_pay"] / summary["total_ride_cost"])) * 100, 2).astype(str) + "%"
    summary["avg_company_revenue_per_ride"] = round(summary["company_revenue"] / summary["num_rides"], 2)
    # Remove columns that aren't needed anymore
    summary.drop(["total_miles"], axis=1, inplace=True)
    return summary
def calculate_monthly_stats(file_dir: str) -> pd.DataFrame:
    # Read data from multiple Parquet files
    files = [os.path.join(file_dir, f) for f in os.listdir(file_dir) if f.endswith(".parquet")]
    
    df = pd.DataFrame()
    for file in files:
        print(file)
        file_stats = calculate_monthly_stats_per_file(file_path=file)
        # Check if df is empty
        if df.empty:
            df = file_stats
        else:
            # Concat row-wise
            df = pd.concat([df, file_stats], axis=0)
    # Sort the dataset
    df = df.sort_values(by="ride_period")
    # Change column order
    cols = ["ride_period", "num_rides", "ride_time_in_days", "total_miles_in_mil", "total_ride_cost",
            "total_rider_pay", "company_revenue", "company_margin", "avg_company_revenue_per_ride"]
    return df[cols]
if __name__ == "__main__":
    data_dir = "nyc-taxi-data"
    output_dir = "pipeline_results"
    output_file_name = "results_pandas.csv"
    # Run the pipeline
    monthly_stats = calculate_monthly_stats(file_dir=data_dir)
    # Save to CSV
    monthly_stats.to_csv(f"{output_dir}/{output_file_name}", index=False)

Implementasi pipeline di DuckDB semoga selesai tanpa masalah memori.

Kode: DuckDB

Anda sudah familier dengan sebagian besar kode DuckDB.

Satu-satunya hal baru adalah SELECT teratas, karena menghitung beberapa statistik tambahan. Sisanya saya biarkan apa adanya:

import duckdb
import pandas as pd
def calculate_monthly_stats(file_dir: str) -> pd.DataFrame:
    query = f"""
        SELECT
            ride_period,
            num_rides,
            ride_time_in_days,
            total_miles / 1000000 AS total_miles_in_mil,
            total_ride_cost,
            total_rider_pay,
            ROUND(total_ride_cost - total_rider_pay, 2) AS company_revenue,
            ROUND((1 - total_rider_pay / total_ride_cost) * 100, 2) || '%' AS company_margin,
            ROUND((total_ride_cost - total_rider_pay) / num_rides, 2) AS avg_company_revenue_per_ride
        FROM (
            SELECT
                ride_year || '-' || ride_month AS ride_period,
                COUNT(*) AS num_rides,
                ROUND(SUM(trip_time) / 86400, 2) AS ride_time_in_days,
                ROUND(SUM(trip_miles), 2) AS total_miles,
                ROUND(SUM(base_passenger_fare + tolls + bcf + sales_tax + congestion_surcharge + airport_fee + tips), 2) AS total_ride_cost,
                ROUND(SUM(driver_pay), 2) AS total_rider_pay
            FROM (
                SELECT
                    DATE_PART('year', pickup_datetime) AS ride_year,
                    DATE_PART('month', pickup_datetime) AS ride_month,
                    trip_time,
                    trip_miles,
                    base_passenger_fare,
                    tolls,
                    bcf,
                    sales_tax,
                    congestion_surcharge,
                    airport_fee,
                    tips,
                    driver_pay
                FROM PARQUET_SCAN("{file_dir}/*.parquet")
                WHERE 
                    ride_year = 2024
                AND ride_month >= 1 
                AND ride_month <= 6
            )
            GROUP BY ride_period
            ORDER BY ride_period
        )
    """
    conn = duckdb.connect()
    df = conn.sql(query).df()
    conn.close()
    return df
if __name__ == "__main__":
    data_dir = "nyc-taxi-data"
    output_dir = "pipeline_results"
    output_file_name = "results_duckdb.csv"
    # Run the pipeline
    monthly_stats = calculate_monthly_stats(file_dir=data_dir)
    # Save to CSV
    monthly_stats.to_csv(f"{output_dir}/{output_file_name}", index=False)

Berikutnya, saya akan menunjukkan perbedaan runtime-nya.

Hasil perbandingan performa

Setelah menjalankan kedua pipeline 5 kali dan merata-ratakan hasilnya, berikut runtime yang saya dapatkan:

Bagan perbandingan runtime DuckDB vs pandas

Bagan perbandingan runtime DuckDB vs. pandas

pandas rata-rata 24 kali lebih lambat daripada DuckDB saat memuat dan memproses sekitar 120 juta baris (~3GB), tersebar di 6 file Parquet.

Perbandingan ini tidak sepenuhnya adil, karena saya tidak bisa memproses semua file Parquet sekaligus dengan pandas. Meski begitu, hasilnya tetap relevan karena Anda akan menghadapi kesulitan yang sama di pekerjaan sehari-hari. 

Jika satu pustaka tidak bisa melakukan yang Anda butuhkan, cobalah yang lain. Dan yang kemungkinan besar akan selesai paling cepat hampir setiap saat adalah DuckDB.

Praktik Terbaik Menggunakan DuckDB dalam Pipeline Data

Sebelum membiarkan Anda mengeksplorasi DuckDB lebih jauh sendiri, saya ingin berbagi beberapa praktik terbaik umum dan terkait data engineering:

  • Selalu coba gunakan fungsi bawaan DuckDB terlebih dahulu: Anda dapat membawa fungsi Python Anda ke DuckDB dengan user-defined functions, yang membawa fleksibilitas DuckDB ke dimensi yang benar-benar berbeda. Salah satu praktik terbaik umum dalam pemrograman adalah tidak menciptakan kembali roda. Dengan kata lain, Anda tidak ingin mengimplementasikan fungsionalitas dari nol jika sudah ada.
  • Optimalkan format file terlebih dahulu: Hanya karena DuckDB menawarkan peningkatan performa signifikan sejak awal, bukan berarti Anda harus menghentikan upaya optimasi. Anda telah melihat seberapa jauh lebih lambat DuckDB saat membaca file CSV (hingga 600 kali) dibandingkan Parquet. Yang terakhir akan selalu lebih cepat dibaca dan memakan ruang disk lebih sedikit. Sama-sama untung. 
  • Jangan gunakan DuckDB untuk tujuan transaksional: DuckDB adalah database OLAP (Online Analytical Processing), yang berarti dioptimalkan untuk pernyataan SELECT. Anda sebaiknya menghindarinya pada alur kerja yang bergantung pada pernyataan INSERT dan UPDATE yang sering. Jika demikian, gunakan SQLite sebagai gantinya.
  • Manfaatkan dukungan native DuckDB di Jupyter Notebook: Pengguna Jupyter Lab/Notebook dapat menjalankan kueri DuckDB secara langsung, tanpa perlu menggunakan fungsi Python spesifik. Ini cara yang bagus untuk mengeksplorasi data lebih cepat dan menjaga notebook tetap rapi.
  • Selalu ingat bagaimana DuckDB menangani konkurensi: Anda dapat mengonfigurasi DuckDB sehingga satu proses dapat membaca dan menulis ke database, atau sehingga banyak proses dapat membaca darinya, tetapi tidak ada yang bisa menulis. Yang pertama memungkinkan caching data di RAM untuk kueri analitik yang lebih cepat. Secara teori banyak proses bisa menulis ke file database yang sama, tetapi untuk mencapainya Anda perlu mengimplementasikan logika kunci mutex lintas-proses dan membuka/menutup database secara manual.
  • Anda dapat menggunakan DuckDB di cloud: Proyek MotherDuck adalah gudang data kolaboratif yang membawa dan memperluas kekuatan DuckDB ke cloud. Ini mungkin layak dieksplorasi bagi data engineer.

Penutup

Sebagai kesimpulan, menurut saya Anda harus mencoba DuckDB jika Anda adalah data engineer yang bertanggung jawab membangun dan mengoptimalkan pipeline data.

Anda tidak rugi apa pun dan berpotensi mendapatkan banyak. Alat ini hampir pasti lebih cepat daripada apa pun yang ditawarkan Python. Ini berjalan sangat cepat di laptop Anda dan memungkinkan Anda menelusuri dataset yang jika tidak akan menimbulkan galat memori. Bahkan dapat terhubung ke platform penyimpanan cloud ketika Anda tidak ingin atau tidak diizinkan menyimpan data secara lokal.

Meski begitu, DuckDB bukan satu-satunya optimasi yang perlu Anda hadirkan. Anda harus selalu berusaha mengoptimalkan data yang masuk ke sistem Anda. Misalnya, meninggalkan CSV dan beralih ke Parquet akan menghemat waktu komputasi dan penyimpanan. Optimasi lain yang perlu Anda pertimbangkan adalah menerapkan prinsip arsitektur data modern ke dalam alur kerja dan pipeline Anda.

DuckDB hanyalah sebuah alat. Namun sangat kuat.

FAQs

Apakah DuckDB gratis digunakan?

Ya, DuckDB adalah proyek sumber terbuka. Anda bisa mengunduhnya, memodifikasinya, dan bahkan berkontribusi melalui GitHub.

Apa perbedaan DuckDB dengan SQLite?

DuckDB dioptimalkan untuk beban kerja analitik melalui kueri SQL yang kompleks (pikirkan pernyataan SELECT), sedangkan SQLite lebih cocok untuk pemrosesan transaksional (pikirkan pernyataan INSERT dan UPDATE).

Apakah DuckDB adalah database NoSQL?

Bukan, DuckDB adalah sistem manajemen basis data SQL OLAP (Online Analytical Processing) in-process.

Apakah DuckDB lebih cepat daripada pandas?

Dalam hampir semua skenario, DuckDB akan lebih cepat daripada pandas, seringnya selisih ordo besaran (setidaknya). DuckDB juga memungkinkan Anda bekerja dengan dataset yang akan memunculkan galat memori di pandas.

Apakah DuckDB menggunakan dialek SQL khusus?

Tidak, Anda akan langsung familier jika memiliki pengetahuan SQL dasar. DuckDB sangat mengikuti konvensi dialek PostgreSQL, tetapi meskipun Anda terbiasa dengan vendor database lain, praktis tidak ada kurva pembelajaran.


Dario Radečić's photo
Author
Dario Radečić
LinkedIn
Senior Data Scientist yang berbasis di Kroasia. Penulis Tekno Teratas dengan lebih dari 700 artikel yang telah diterbitkan, menghasilkan lebih dari 10 juta tayangan. Penulis buku Machine Learning Automation with TPOT.
Topik
Rekayasa Data
Python

Pelajari lebih lanjut tentang data engineering dengan kursus-kursus ini!

Kursus

Memahami Rekayasa Data

2 jam
378K
Temukan bagaimana data engineer meletakkan dasar yang memungkinkan data science terlaksana. Tidak perlu coding!
Lihat DetailRight Arrow
Mulai Kursus
Lihat SelengkapnyaRight Arrow
Terkait

blogs

40 Pertanyaan Wawancara DBMS Teratas di 2026

Kuasai pertanyaan wawancara basis data, dari konsep SQL dasar hingga skenario desain sistem tingkat lanjut. Panduan mendalam ini mencakup semua yang Anda perlukan untuk sukses di wawancara DBMS dan meraih peran berikutnya.
Dario Radečić's photo

Dario Radečić

15 mnt

blogs

Spaghetti Plot dan Jalur Badai

Temukan alasan mengapa Anda sebaiknya (tidak) menggunakan spaghetti plot untuk menyampaikan ketidakpastian jalur prediksi badai serta dampaknya terhadap interpretasi.
Hugo Bowne-Anderson's photo

Hugo Bowne-Anderson

13 mnt

blogs

Tutorial Korelasi di R

Dapatkan pengenalan dasar-dasar korelasi di R: pelajari lebih lanjut tentang koefisien korelasi, matriks korelasi, plotting korelasi, dan sebagainya.
David Woods's photo

David Woods

13 mnt

blogs

12 Alternatif ChatGPT Terbaik yang Bisa Anda Coba pada 2026

Artikel ini menyajikan daftar alternatif ChatGPT yang akan meningkatkan produktivitas Anda.
Javier Canales Luna's photo

Javier Canales Luna

14 mnt

Lihat SelengkapnyaLihat Selengkapnya