AWSでの効率的なデータ処理を考える~データフォーマット~
はじめに
データ処理においては、データフォーマットの選択がパフォーマンスやコストに大きな影響を与えます。
これはAWSにおいても同様で、AWSでのデータ基盤構築にあたり重要な要素の一つとなります。
今回は主要なファイル形式の特徴とAWS Lambdaを活用したフォーマット変換、そしてデータ基盤で採用されることの多いメダリオンアーキテクチャへの適用について解説したいと思います。
データフォーマットの特徴とパフォーマンス
前述したようにデータフォーマットの選択はデータの圧縮率、クエリ性能、ストレージコスト、データの可搬性などに大きな影響を与えます。
ここでは、主要なデータフォーマット(CSV、Parquet、Avro)についてどのような特徴があるのか、そしてパフォーマンスの観点について整理します。
CSV
CSVは、テキストベースのシンプルなデータフォーマットであり、多くのシステムやツールでサポートされています。
よく使われるデータフォーマットではあるのですが、エスケープ方法、文字列のクォート、デリミタ等の要素が柔軟(悪く言えば統一されていない)で、データ処理においては性能があまり出ないフォーマットとなっています。
-
メリット
- 人間が直接読めるため、デバッグや手動のデータ編集が容易
- ほぼすべてのプログラム言語とツールがサポート
- データのエクスポートやインポートに広く使用されている
-
デメリット
- 他のフォーマットと比較し、ファイルサイズが大きくなる(圧縮されていない)
- スキーマ情報を持っていないため、データ解析時にカラムの型や区切りを考慮する必要がある
- 列指向処理ができないため、大規模データの分析には不向き
Parquet
Parquetは、Apache Parquetプロジェクトで開発された列指向のデータフォーマットとなります。
データをカラム型フォーマットで保存し、データレイク環境において優れた読み書き性能を実現するように設計されています。
データにはスキーマ情報が組み込まれており、さらに可搬性が高いという特徴があります。
Parquetは後述するAvroとともに、新世代のファイルフォーマットと呼ばれ、CSVの相互運用性の低さやJSONの性能の低さを大幅に改善し、現在普及の進んでいるフォーマットとなります。
AWSでのデータ処理に使われる、AWS GlueやEMRでも使用可能なフォーマットです。
-
メリット
- スキーマ情報を持っており、スキーマの進化をサポート
- 列単位でデータを保存するため、必要なカラムのみを読みこむことが可能で効率的な処理が可能
- 非常に高い圧縮率をもつ
- 分割(パーティショニング)や圧縮を活用することで、ストレージと処理コストを削減可能
-
デメリット
- テキストとして直接編集が困難
- 書き込み時のオーバーヘッドが大きいため、ストリーミングデータでは後述のAvroより性能が落ちる
Avro
AvroはApache Avroプロジェクトで開発されたRPCやデータシリアライズのための行指向のデータフォーマットとなります。
スキーマを埋め込むことができ、スキーマメタデータはJSONで指定されます。
AvroはHadoopで広く使用され、さまざまなクラウドデータツールでも利用できます。
AWSでのデータ処理に使われる、AWS GlueやEMRでも使用可能なフォーマットです。
-
メリット
- スキーマ情報を持っており、スキーマの進化をサポート
- Kafkaなどのストリーム処理と相性が良い
- シリアライズ/デシリアライズが高速
-
デメリット
- バイナリフォーマットのため人間が直接読めない
- Parquetと比較するとクエリ最適化は限定的(主にストリーミング用途)
Lambdaでデータフォーマットを変換する(Parquet)
今回はデータレイク環境において優れた読み書き性能を発揮するParquetへの変換処理をLambdaで実装してみます。
S3へのcsvファイルアップロードをトリガにデータフォーマット変換Lambdaを起動し、Parquetへ変換し、別のS3バケットに格納するものとなります。
レイヤーの作成
使用するライブラリは以下となります。
- DuckDB:データベースエンジンとして、メモリ内での高速なSQLクエリ処理を実現。
- PyArrow:Arrowフォーマットを使用し、高速なデータの変換・転送をサポート。
下記のコマンドでzipに固めてレイヤーに追加してください。
pip install -t python --platform manylinux2014_x86_64 --only-binary=:all: pyarrow
pip install -t python --platform manylinux2014_x86_64 --only-binary=:all: duckdb
サンプルコード
import duckdb
import boto3
import pyarrow as pa
import pyarrow.parquet as pq
from io import BytesIO
def lambda_handler(event, context):
try:
# 1. DuckDB接続とホームディレクトリ設定
duckdb_connection = duckdb.connect(database=':memory:')
duckdb_connection.execute("SET home_directory='/tmp'") # Lambdaの一時ディレクトリに設定
# httpfs 拡張モジュールのインストールとロード
duckdb_connection.execute("INSTALL httpfs;")
duckdb_connection.execute("LOAD httpfs;")
# 2. S3からCSVを読み込む
s3_bucket = event['Records'][0]['s3']['bucket']['name']
s3_object_key = event['Records'][0]['s3']['object']['key']
s3_input_path = f"s3://{s3_bucket}/{s3_object_key}"
print(f"Processing file: {s3_input_path}")
query = f"""
SELECT * FROM read_csv_auto('{s3_input_path}')
"""
result_arrow_table = duckdb_connection.execute(query).fetch_arrow_table()
print(f"Loaded {result_arrow_table.num_rows} rows from CSV")
# 3. Parquetに変換
output_buffer = BytesIO()
pq.write_table(result_arrow_table, output_buffer)
output_buffer.seek(0)
# 4. S3にアップロード
s3_output_bucket = "任意のバケット"
s3_client = boto3.client('s3')
output_key = s3_object_key.replace('.csv', '.parquet')
s3_output_path = f"s3://{s3_output_bucket}/{output_key}"
s3_client.upload_fileobj(output_buffer, s3_output_bucket, output_key)
print(f"File converted and saved to {s3_output_path}")
except Exception as e:
print(f"Error occurred: {e}")
テストデータ
今回はKaggleのNYC Yellow Taxi Trip Dataを使ってみます。
サイズは1.9GBの大きさとなっており、parquet形式に変換することでファイルサイズの圧縮と後続プロセスでの処理効率化を図ります。
実行結果
変換前
変換後
メダリオンアーキテクチャに組み込む
メダリオンアーキテクチャとは
メダリオンアーキテクチャとはデータを論理的に整理するためのデータアーキテクチャで、データのレイヤー(ブロンズ⇒シルバー⇒ゴールド)を遷移するごとにデータの構造と品質を洗練・向上させることを目的にしています。
データが順次処理されることからマルチホップアーキテクチャとも呼ばれています。
下記の記事で詳細を解説しています。
各レイヤーの概要
- Bronze層:生データをそのまま保持する層
- Silver層:前処理やフォーマット変換を行い、利用しやすい形式に整形する層
- Gold層:最終的な分析やビジネスインテリジェンス向けに集約・加工したデータを配置する層
変換レイヤーの追加
メダリオンアーキテクチャでもデータ処理を効率的に行うために、データフォーマットが重要な要素となります。
対向サーバからのデータがcsvやjson等非効率なフォーマットの場合、より効率的なフォーマットに変換することで全体的なパフォーマンスを向上させることができます。
メダリオンアーキテクチャへの組み込みの際は上記のようにBronze層の一つ後ろに追加するのがいいと思います。
ここでParquet形式に変換することで後続のSilver層での処理を効率的に行うことができます。
メダリオンアーキテクチャにおけるParquet変換のメリット
Bronze層にあるCSVやJSONなどの非効率なフォーマットのデータを、Silver層においてParquetに変換することで以下のようなメリットがあります。
- ストレージコストの削減
- クエリ性能の向上
- 後続処理の効率化
AWSの各サービス(Lambda、Glue、EMR)を利用する場合、データ量や処理時間、運用コストを踏まえて最適なサービスを選定することが必要となります
たとえば、コストの低さが求められる小規模ETLであればLambda、大規模なバッチ処理であればGlueやEMRが候補となります。
サービス選定のポイント
下記に他の記事でまとめた各サービスのメリット・デメリットとユースケースを再掲します。
AWS Lambda
- メリット:サーバレスで自動でスケーリングが可能となります。また設定や管理の手間が比較的少なくコスト効率に優れた構成とすることができます。
- デメリット:実行時間の上限が最大15分になるので大規模データを扱う場合、タイムアウトとなってしまう可能性があります。またメモリやCPU等の処理性能的に複雑なデータを扱う場合には注意が必要となります。
- ユースケース:小規模なデータ変換を行うETLに適しています。また、S3トリガーを活用することでリアルタイムで処理を実行することができます。
AWS Glue
- メリット:サーバレスで自動でスケーリングが可能となります。Sparkをベースにしたジョブを構成することができるため、高速かつ効率的に大規模データを処理することが可能となります。データカタログによるスキーマ管理や様々なデータソースと連携が比較的に容易に実施できます。
- デメリット:チューニングできる範囲が限られるため、用途によってはマッチしない可能性があります。
- ユースケース:大規模データのバッチ処理やメダリオンアーキテクチャの各レイヤーの処理に最適なサービスとなります。
Amazon EMR
- メリット:SparkのほかHadoop等のデータ処理エンジンを選択することができます。他のサービスと同様にスケールアウトも可能で、さらにカスタマイズ性も高いため様々なユースケースに対応することが可能となります。
- デメリット:他のサービスと比較するとクラスターのセットアップや管理が煩雑となるため、運用・保守コストが高くなる可能性があります。
- ユースケース:複雑なデータ処理や大規模なバッチ処理が必要な場合に適したサービスとなります。より特定業務向けに高度な処理を行いたい場合にはGlueよりも優れた性能となる可能性があります。
このようにETL処理はLambda、Glue、EMRが候補となります。
基本的にはETLに特化したGlueをベースに検討して、データ量や処理時間を考慮しながら適切なサービスを選定していただければと思います。
まとめ
今回はデータフォーマットの概要とAWS Lambdaを活用してParquet形式に変換する処理を紹介しました。
近年、データ量が膨大となり、いかに効率的に処理するかが大きなポイントになっています。
データフォーマットの選択はデータ処理の効率性に直結します。
CSV、Parquet、Avroそれぞれの特性を理解し、適切に活用することでAWS上でのデータ処理を最適化することが可能となります。
以前紹介したメダリオンアーキテクチャでもrawデータを一度Parquet形式に変換することで、silver層以降の処理を効率的に行うことができます。
要件や制約等様々な要素を加味して適切なフォーマットを選択していただければと思います。
今回の記事が最適なアーキテクチャ設計の参考になれば幸いです。
追記
Parquet形式への変換方法について検証しました。
下記の記事にまとめているので参考にどうぞ。
Discussion