🦆

AWS Lambda×DuckDBで軽量ETLを構築してみた

2024/09/20に公開

はじめに

個人的にデータエンジニアリングについて考える機会が増えています。

https://zenn.dev/penginpenguin/articles/c3cfd309dc4f78

https://zenn.dev/penginpenguin/articles/741bf19ea03a5f

その中でAWS上での小規模ETLの構築ができないかと考えました。
一般的にはGlueやLambdaが候補となりますが、Glueの場合、小規模データだとコストと見合わず、Lambdaだと処理能力や実行時間の上限が課題となります。
とはいえLambdaのサーバレスでの柔軟な処理は大変魅力的です。

そこで今回はLambdaとDuckDBを組み合わせてシンプルで効率的なETLを構築する方法を紹介します。
DuckDBは軽量ですが強力なクエリエンジンを提供してくれるためLambdaでETLを構築する場合に高速かつ効率的なデータ処理が可能となります。

DuckDBとは

DuckDBとは、組み込み型のOLAP(オンライン分散処理)向けデータベースエンジンです。
DuckDBは非常に軽量で、インメモリ処理が可能となるためLambdaのようなシンプルな環境でも効率的に動作させることができます。
特にデータ分析やETL処理のようなバッチ処理において強力なパフォーマンスを発揮すると言われています。

https://duckdb.org/

処理イメージ

今回はS3に格納したCSVからLambdaでデータを抽出し、抽出したデータをS3に格納するというちょっとしたETLを作成してみます。

CSVはKaggleでおなじみのTitanicのtraining set (train.csv)を活用します。

https://www.kaggle.com/c/titanic/data

このデータから年齢が20歳以下のデータを抽出してCSVを作成してみます。

Lambdaへの組み込み

Lambdaへの組み込みはLambda レイヤーを使うのが手っ取り早いです。

https://github.com/tobilg/duckdb-nodejs-layer

サンプルコード

今回は動作確認をしたいだけなのでqueryをハードコーディングしています。
S3トリガーを使用すればバケット名とファイル名が取れるのでパラメータとして渡せば実用できると思います。

index.mjs
import DuckDB from "duckdb";

// Instantiate DuckDB
const duckDB = new DuckDB.Database(":memory:");

// Create connection
const connection = duckDB.connect();

// Promisify query method
const query = (query) => {
  return new Promise((resolve, reject) => {
    connection.all(query, (err, res) => {
      if (err) reject(err);
      resolve(res);
    });
  });
};

// Lambda handler function
export const handler = async (event) => {
  try {
    // Will show DuckDB version

    await query("SET home_directory='/tmp'");

    await query(
      `COPY (SELECT * FROM read_csv('s3://duckdb-test20240920/train.csv') WHERE Age <= 20) TO 's3://duckdb-test20240920/train2.csv'`
    );

    return {
      statusCode: 200,
      body: JSON.stringify({result: "OK" }),
    };
  } catch (error) {
    return {
      statusCode: 500,
      body: JSON.stringify({ error: error.message }),
    };
  }
};

実行結果

下記のようにtrain2.csvが作成される。

こんな感じにWHERE句の条件でフィルタされたデータが格納されました。

Lambda×DuckDBのメリット・デメリット

メリット

  • 導入が簡単:Lambdaレイヤーで簡単に導入可能。
  • Lambdaで簡単に開発可能:DuckDBにて提供されている関数を活用することで簡単に開発が可能。
  • SQLライクな処理:SQLの構文(PostgreSQL互換)で簡単に操作できる。
  • 効率的な処理が可能:Lambda上でもインメモリ処理により効率的なデータ処理が可能。
  • S3トリガーでリアルタイムETL処理が可能:S3トリガーを使うことでファイル格納をトリガーにETL処理を即時実行可能。

デメリット

  • 最大メモリサイズの制限がある:Lambdaの制限により最大メモリサイズが10240 MBとなる。場合によってはメモリが枯渇する可能性がある。(とはいえ意外と大規模データセットでも耐えられるみたいです。)
  • 実行時間の制限がある:Lambdaの制限により実行時間は15分が最大となります。大規模データセットの場合、15分を超えてしまう可能性があるため、場合によってはコンテナサービス等も検討する必要があります。LLRTを活用することで処理を改善できると思います。

https://zenn.dev/penginpenguin/articles/fe42ba481ab5a1

まとめ

今回は軽量なデータセットでのお試し程度でしたが、非常に簡単に導入することができました。
AWSでのETLといえばGlueやEMRが候補として考えられますが、小規模なデータセットの場合、コストが見合わないことが考えられます。
そういった場合に今回のLambda×DuckDBはコストパフォーマンスに優れた有力な候補となると考えられます。
また、Lambdaということもあり、S3トリガーで動作させることも可能となりますので、軽量なリアルタイムETLとして活用可能だと思います。

また、ETL以外にもAthenaの代わりに使ったり、様々な用途で活用が期待できます。
Lambda関数URLを使ってqueryの中身をパラメータとして渡して、レスポンスに取得データを設定すればAthenaもどきができそうな気がします。

今回の記事が小規模データ処理の実装の参考になると幸いです。

Discussion