ログデータのETLを高速化した話

この記事は Nature Remo Advent Calendar 2022 の9日目として書きました。

adventar.org

データエンジニアの原です。 皆さん、ETL、ELT に取り組んでいますか?今回は、API ログのETL を実装する上で困った点、改善した点を紹介します。

API ログをETL しよう

Nature ではデータ基盤として、BigQuery (以下、BQ)とArgo Workflow を中心にデータ基盤を構築しています。 DB のデータはEmbulk で、ログデータなどはGlue でETL を実施し、BQ にデータを転送しています。

engineering.nature.global

今回の取り組みでは、API のログデータをETL して、BQ でクエリを投げられるようにすることが目的になります。

背景

Nature では、CS メンバーがユーザからのお問い合わせに対してサービス利用状況をチェックできるように、いくつかのログが混ざった状態で置かれており、そこに対してAthena でクエリできる状態になっています。 しかし、その状態では1回のクエリの実行時間が長くなってしまい、任意のユーザの任意の時間帯の操作状況を知る。といったスポット的なクエリであればまだしも、複数日またいだ調査をする場合は使い勝手に課題があるという状態でした。 また、実際にどの機能が良く使われているかなどを知るためにAPI request だけをスキャンしたいが、他のデータも含めて必要以上のデータをスキャンすることになり、コストもかかってしまいます。

そこで、今回はそのデータレイクの中からAPI へのrequests データだけを抜粋し、不要な項目や個人情報などを除去しBQ にロードすることが目的となりました。

意気揚々と既存と同じ方針ですすめた

他のタスクでGlue を用いたログデータの整形、BQ へのロードを実施するコンテナとジョブを用意していたので、問題なく済むと思っていました。 実際、Glue のETL スクリプトを実装し、アウトプットをBQ へロードするようにジョブを組んでworkflow を実行しました。 しかし、いつもなら2時間強でSlack に送られてくるジョブ完了通知がいつまで経ってもきません。 よくよく実行時間を確認すると10時間という衝撃の時間がかかっていました。

現状を分析する

現状のタスクは上記にも触れているように、2つの要素から構成されます。

  1. Glue によるデータ整形
  2. BQ へのロード

それぞれで時間を分解したところ、Glue によるデータ整形がおよそ4時間弱、データ転送が6時間強という内訳でした。 時間短縮に大きく貢献するのは、データ転送部分であるものの、双方ともに時間短縮できる要素がありそうだったのでそれぞれ原因を分析し改善することにしました。

Glue の改善

Glue の改善として目をつけていたのは、以下の3つです。

  1. データのフィルタリング処理
  2. partition 数
  3. ノード台数

1つ目は、Glue 上のTransform で効率よく実施されるようにrow に対する処理を相互に影響を及ぼさない範囲でまとめるなどの工夫をしました。 2つ目は、partition 数は最終的にparquet として保存するときのファイル数になります。この部分は台数とも関係すると思われますが、partition 数が少ないと1つに集約するのに時間がかかります。 したがって、3つ目は上記の部分と合わせて調整が必要ですが、実験の段階では1と2(主に2)の処理時間を計測するために、ワーカ数を10台で固定し実験を繰り返しました。 特にpartition 数が影響を及ぼしていることが明らかになりました。

パーティション数の違いによる実行時間の差

上記の画像は実験時のGlue の実行時間の実験です。 パーティションを指定しない場合、parquet ファイルは3499ファイル程度生成され、実行時間としては多少の誤差はあるものの、最短の場合、2時間50分で最短の実行にあることが確認できました。 また、他のタスクでは1つのparquet ファイルにまとめていましたが、今回のタスクの場合、そこが実行時間が延びている原因の一つであることが明らかになりました。

データ転送の改善

データの転送は gsutil CLI を使って転送していました。このデータ転送はS3 からGCS にファイルを転送するものになっており、gsutil はS3 の内容とGCS の内容をrsync できるので利用していたという背景があります。

このタスクを見直す上でポイントは以下の2つかと考えていました。

  1. マルチプロセスでファイルを転送する
  2. gsutil を置き換える

今回最もポイントになったのは、gsutil を gcloud storage に置換したことだと思います。以下の記事を参考に1ファイルでも複数ファイルでも速く転送できることを期待して置換しました。

cloud.google.com

しかし、ここで問題が起きました。上記のpartition 数の修正で100ファイルよりも多い数を実験していたときに、転送するプロセスが正常に終了しなかったり、転送されたファイルが歯抜けになる。といった問題が生じました。ファイル数を調整しながら実験した結果、10ファイルが上限であることが確認できました。 そこで、ファイル数を1,5,10 と変更した場合に、全体としてどの程度の処理時間に落ち着くのかを比較しました。

全体の処理時間

以下に実験していたときの比較時間を載せて起きました。

API requests のETL 合計時間

結果として10ファイルとして実行する場合、API requests のETL に関して最も時間が短くなり、4時間30分まで短くすることができました。

現状プロダクション環境では、ワーカ数を20台にして実行しているところ、Glue のタスクは100分程度、データ転送は68分程度、合計2時間50分程度まで短縮する事ができました。

終わりに

タスクごとにボトルネックを特定しながら、処理時間を大きく削減することができました。 うまく削減することができ、よい貢献ができたと思っています。 データの品質を高めるためにsbt などの最新技術に今後取り組めたらと思っていますので、もしご興味があるかたはカジュアル面談でお話しましょう!

herp.careers